diff options
author | Thom May <thom@chef.io> | 2017-12-12 17:32:48 +0000 |
---|---|---|
committer | Thom May <thom@chef.io> | 2017-12-13 10:17:15 +0000 |
commit | 65109e8b87c0493b76d64622b8e57679b7b909d2 (patch) | |
tree | 6ac6fc5c72b70d53069b5748bb589804c67c168c /chef-config/lib | |
parent | 2ed7c7be81b930e99affb139f1854309d55fabb5 (diff) | |
download | chef-65109e8b87c0493b76d64622b8e57679b7b909d2.tar.gz |
implement credential management
Signed-off-by: Thom May <thom@chef.io>
Diffstat (limited to 'chef-config/lib')
-rw-r--r-- | chef-config/lib/chef-config/config.rb | 7 | ||||
-rw-r--r-- | chef-config/lib/chef-config/mixin/credentials.rb | 57 | ||||
-rw-r--r-- | chef-config/lib/chef-config/workstation_config_loader.rb | 34 |
3 files changed, 97 insertions, 1 deletions
diff --git a/chef-config/lib/chef-config/config.rb b/chef-config/lib/chef-config/config.rb index beb78f25d0..63dd4ecda2 100644 --- a/chef-config/lib/chef-config/config.rb +++ b/chef-config/lib/chef-config/config.rb @@ -592,6 +592,12 @@ module ChefConfig # If chef-zero is enabled, this defaults to nil (no authentication). default(:client_key) { chef_zero.enabled ? nil : platform_specific_path("/etc/chef/client.pem") } + # A credentials file may contain a complete client key, rather than the path + # to one. + # + # We'll use this preferentially. + default :client_key_contents, nil + # When registering the client, should we allow the client key location to # be a symlink? eg: /etc/chef/client.pem -> /etc/chef/prod-client.pem # If the path of the key goes through a directory like /tmp this should @@ -631,6 +637,7 @@ module ChefConfig default(:validation_key) { chef_zero.enabled ? nil : platform_specific_path("/etc/chef/validation.pem") } default :validation_client_name, "chef-validator" + default :validation_key_contents, nil # When creating a new client via the validation_client account, Chef 11 # servers allow the client to generate a key pair locally and send the # public key to the server. This is more secure and helps offload work from diff --git a/chef-config/lib/chef-config/mixin/credentials.rb b/chef-config/lib/chef-config/mixin/credentials.rb new file mode 100644 index 0000000000..4c0192fff8 --- /dev/null +++ b/chef-config/lib/chef-config/mixin/credentials.rb @@ -0,0 +1,57 @@ +# +# Copyright:: Copyright 2017, Chef Software, 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 "tomlrb" +require "chef-config/path_helper" + +module ChefConfig + module Mixin + module Credentials + + def load_credentials(profile = nil) + credentials_file = PathHelper.home(".chef", "credentials").freeze + context_file = PathHelper.home(".chef", "context").freeze + + return unless File.file?(credentials_file) + + context = File.read(context_file) if File.file?(context_file) + + environment = ENV.fetch("CHEF_PROFILE", nil) + + profile = if !profile.nil? + profile + elsif !environment.nil? + environment + elsif !context.nil? + context + else + "default" + end + + config = Tomlrb.load_file(credentials_file) + apply_credentials(config[profile], profile) + rescue ChefConfig::ConfigurationError + raise + rescue => e + # TOML's error messages are mostly rubbish, so we'll just give a generic one + message = "Unable to parse Credentials file: #{credentials_file}\n" + message << e.message + raise ChefConfig::ConfigurationError, message + end + end + end +end diff --git a/chef-config/lib/chef-config/workstation_config_loader.rb b/chef-config/lib/chef-config/workstation_config_loader.rb index babb78aeb8..4c07cac702 100644 --- a/chef-config/lib/chef-config/workstation_config_loader.rb +++ b/chef-config/lib/chef-config/workstation_config_loader.rb @@ -22,19 +22,23 @@ require "chef-config/logger" require "chef-config/path_helper" require "chef-config/windows" require "chef-config/mixin/dot_d" +require "chef-config/mixin/credentials" module ChefConfig class WorkstationConfigLoader include ChefConfig::Mixin::DotD + include ChefConfig::Mixin::Credentials # Path to a config file requested by user, (e.g., via command line option). Can be nil attr_accessor :explicit_config_file + attr_reader :profile # TODO: initialize this with a logger for Chef and Knife - def initialize(explicit_config_file, logger = nil) + def initialize(explicit_config_file, logger = nil, profile: nil) @explicit_config_file = explicit_config_file @chef_config_dir = nil @config_location = nil + @profile = profile @logger = logger || NullLogger.new end @@ -62,6 +66,7 @@ module ChefConfig end def load + load_credentials(profile) # Ignore it if there's no explicit_config_file and can't find one at a # default path. if !config_location.nil? @@ -138,6 +143,33 @@ module ChefConfig a end + def apply_credentials(creds, _profile) + Config.node_name = creds.fetch("node_name") if creds.key?("node_name") + Config.node_name = creds.fetch("client_name") if creds.key?("client_name") + Config.chef_server_url = creds.fetch("chef_server_url") if creds.key?("chef_server_url") + Config.validation_client_name = creds.fetch("validation_client_name") if creds.key?("validation_client_name") + + extract_key(creds, "validation_key", :validation_key, :validation_key_contents) + extract_key(creds, "validator_key", :validation_key, :validation_key_contents) + extract_key(creds, "client_key", :client_key, :client_key_contents) + end + + def extract_key(creds, name, config_path, config_contents) + return unless creds.has_key?(name) + + val = creds.fetch(name) + if val.start_with?("-----BEGIN RSA PRIVATE KEY-----") + Config.send(config_contents, val) + else + abs_path = Pathname.new(val).expand_path(home_chef_dir) + Config.send(config_path, abs_path) + end + end + + def home_chef_dir + @home_chef_dir ||= PathHelper.home(".chef") + end + def apply_config(config_content, config_file_path) Config.from_string(config_content, config_file_path) rescue SignalException |