chef-config 18.10.17 → 19.1.164
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/chef-config.gemspec +1 -0
- data/lib/chef-config/config.rb +14 -3
- data/lib/chef-config/exceptions.rb +4 -1
- data/lib/chef-config/fips.rb +1 -1
- data/lib/chef-config/logger.rb +1 -1
- data/lib/chef-config/mixin/credentials.rb +125 -6
- data/lib/chef-config/mixin/dot_d.rb +1 -1
- data/lib/chef-config/mixin/fuzzy_hostname_matcher.rb +1 -1
- data/lib/chef-config/mixin/train_transport.rb +19 -11
- data/lib/chef-config/path_helper.rb +6 -5
- data/lib/chef-config/version.rb +2 -2
- data/lib/chef-config/windows.rb +1 -1
- data/lib/chef-config/workstation_config_loader.rb +1 -1
- data/lib/chef-config.rb +1 -1
- data/spec/unit/config_spec.rb +22 -4
- data/spec/unit/credentials_spec.rb +181 -0
- data/spec/unit/fips_spec.rb +1 -1
- data/spec/unit/path_helper_spec.rb +1 -1
- data/spec/unit/workstation_config_loader_spec.rb +1 -1
- metadata +20 -9
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 309b22099435d14e4f8be71a98aff1005772ac64dbf60c440e5d33f4fce10d53
|
|
4
|
+
data.tar.gz: 8c46f82f7c17fdb2330ca0f2182f9e9476b3d7f96c18b13728d8749d1b86831a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c822ebbe0118452e6240c8d1c0818f90a13701694fa3d93fd700d123ce8150130ab911d076409c76989928678c0d3e04326c367499fc3bbea6902239833d8521
|
|
7
|
+
data.tar.gz: 968e6d6a478e2ec8ef34fe3651fb9c7badc6c1b6a5bf59c832a916ee3d48a398104fe6f64c47f275d082c72c609158b3bfdda802f1ced9de4bc1c3de9f9ffab5
|
data/chef-config.gemspec
CHANGED
|
@@ -30,6 +30,7 @@ Gem::Specification.new do |spec|
|
|
|
30
30
|
spec.add_dependency "fuzzyurl"
|
|
31
31
|
spec.add_dependency "addressable"
|
|
32
32
|
spec.add_dependency "tomlrb", "~> 1.2"
|
|
33
|
+
spec.add_dependency "racc"
|
|
33
34
|
|
|
34
35
|
spec.files = %w{Rakefile LICENSE} + Dir.glob("*.gemspec") +
|
|
35
36
|
Dir.glob("{lib,spec}/**/*", File::FNM_DOTMATCH).reject { |f| File.directory?(f) }
|
data/lib/chef-config/config.rb
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
# Author:: AJ Christensen (<aj@chef.io>)
|
|
5
5
|
# Author:: Mark Mzyk (<mmzyk@chef.io>)
|
|
6
6
|
# Author:: Kyle Goodwin (<kgoodwin@primerevenue.com>)
|
|
7
|
-
# Copyright:: Copyright (c)
|
|
7
|
+
# Copyright:: Copyright (c) 2009-2026 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
|
|
8
8
|
# License:: Apache License, Version 2.0
|
|
9
9
|
#
|
|
10
10
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -421,7 +421,14 @@ module ChefConfig
|
|
|
421
421
|
# If your `file_cache_path` resides on a NFS (or non-flock()-supporting
|
|
422
422
|
# fs), it's recommended to set this to something like
|
|
423
423
|
# '/tmp/chef-client-running.pid'
|
|
424
|
-
|
|
424
|
+
# In Target Mode, the node name will be used as a prefix to allow
|
|
425
|
+
# parallel execution of Chef against different targets
|
|
426
|
+
default(:lockfile) do
|
|
427
|
+
prefix = ""
|
|
428
|
+
prefix = "#{ChefConfig::Config.node_name}-" if ChefConfig::Config.target_mode?
|
|
429
|
+
|
|
430
|
+
PathHelper.join(file_cache_path, "#{prefix}#{ChefUtils::Dist::Infra::CLIENT}-running.pid")
|
|
431
|
+
end
|
|
425
432
|
|
|
426
433
|
## Daemonization Settings ##
|
|
427
434
|
# What user should Chef run as?
|
|
@@ -917,7 +924,11 @@ module ChefConfig
|
|
|
917
924
|
default :profile, nil
|
|
918
925
|
|
|
919
926
|
default :chef_guid_path do
|
|
920
|
-
|
|
927
|
+
if target_mode?
|
|
928
|
+
PathHelper.join(config_dir, target_mode.host, "#{ChefUtils::Dist::Infra::SHORT}_guid")
|
|
929
|
+
else
|
|
930
|
+
PathHelper.join(config_dir, "#{ChefUtils::Dist::Infra::SHORT}_guid")
|
|
931
|
+
end
|
|
921
932
|
end
|
|
922
933
|
|
|
923
934
|
default :chef_guid, nil
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#
|
|
2
|
-
# Copyright:: Copyright (c)
|
|
2
|
+
# Copyright:: Copyright (c) 2009-2026 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
|
|
3
3
|
# License:: Apache License, Version 2.0
|
|
4
4
|
#
|
|
5
5
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -23,5 +23,8 @@ module ChefConfig
|
|
|
23
23
|
class ConfigurationError < ArgumentError; end
|
|
24
24
|
class InvalidPath < StandardError; end
|
|
25
25
|
class UnparsableConfigOption < StandardError; end
|
|
26
|
+
class NoCredentialsFound < StandardError; end
|
|
26
27
|
|
|
28
|
+
class UnsupportedSecretsProvider < ConfigurationError; end
|
|
29
|
+
class UnresolvedSecret < ConfigurationError; end
|
|
27
30
|
end
|
data/lib/chef-config/fips.rb
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#
|
|
2
2
|
# Author:: Matt Wrock (<matt@mattwrock.com>)
|
|
3
|
-
# Copyright:: Copyright (c)
|
|
3
|
+
# Copyright:: Copyright (c) 2009-2026 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
|
|
4
4
|
# License:: Apache License, Version 2.0
|
|
5
5
|
#
|
|
6
6
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
data/lib/chef-config/logger.rb
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#
|
|
2
|
-
# Copyright:: Copyright (c)
|
|
2
|
+
# Copyright:: Copyright (c) 2009-2026 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
|
|
3
3
|
# License:: Apache License, Version 2.0
|
|
4
4
|
#
|
|
5
5
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#
|
|
2
|
-
# Copyright:: Copyright (c)
|
|
2
|
+
# Copyright:: Copyright (c) 2009-2026 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
|
|
3
3
|
# License:: Apache License, Version 2.0
|
|
4
4
|
#
|
|
5
5
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -26,6 +26,8 @@ module ChefConfig
|
|
|
26
26
|
# @since 13.7
|
|
27
27
|
# @api internal
|
|
28
28
|
module Credentials
|
|
29
|
+
attr_reader :credentials_config
|
|
30
|
+
|
|
29
31
|
# Compute the active credentials profile name.
|
|
30
32
|
#
|
|
31
33
|
# The lookup order is argument (from --profile), environment variable
|
|
@@ -54,6 +56,11 @@ module ChefConfig
|
|
|
54
56
|
# @since 14.4
|
|
55
57
|
# @return [String]
|
|
56
58
|
def credentials_file_path
|
|
59
|
+
return Chef::Config[:credentials] if defined?(Chef::Config) && Chef::Config.key?(:credentials)
|
|
60
|
+
|
|
61
|
+
env_file = ENV["CHEF_CREDENTIALS_FILE"]
|
|
62
|
+
return env_file if env_file && File.file?(env_file)
|
|
63
|
+
|
|
57
64
|
PathHelper.home(ChefUtils::Dist::Infra::USER_CONF_DIR, "credentials").freeze
|
|
58
65
|
end
|
|
59
66
|
|
|
@@ -68,7 +75,7 @@ module ChefConfig
|
|
|
68
75
|
return nil unless File.file?(credentials_file)
|
|
69
76
|
|
|
70
77
|
begin
|
|
71
|
-
Tomlrb.load_file(credentials_file)
|
|
78
|
+
@credentials_config = Tomlrb.load_file(credentials_file)
|
|
72
79
|
rescue => e
|
|
73
80
|
# TOML's error messages are mostly rubbish, so we'll just give a generic one
|
|
74
81
|
message = "Unable to parse Credentials file: #{credentials_file}\n"
|
|
@@ -85,17 +92,129 @@ module ChefConfig
|
|
|
85
92
|
# @return [void]
|
|
86
93
|
def load_credentials(profile = nil)
|
|
87
94
|
profile = credentials_profile(profile)
|
|
88
|
-
cred_config = parse_credentials_file
|
|
89
|
-
return if cred_config.nil? # No credentials, nothing to do here.
|
|
90
95
|
|
|
91
|
-
|
|
96
|
+
parse_credentials_file
|
|
97
|
+
return if credentials_config.nil? # No credentials, nothing to do here.
|
|
98
|
+
|
|
99
|
+
if credentials_config[profile].nil?
|
|
92
100
|
# Unknown profile name. For "default" just silently ignore, otherwise
|
|
93
101
|
# raise an error.
|
|
94
102
|
return if profile == "default"
|
|
95
103
|
|
|
96
104
|
raise ChefConfig::ConfigurationError, "Profile #{profile} doesn't exist. Please add it to #{credentials_file_path}."
|
|
97
105
|
end
|
|
98
|
-
|
|
106
|
+
|
|
107
|
+
resolve_secrets(profile)
|
|
108
|
+
|
|
109
|
+
apply_credentials(credentials_config[profile], profile)
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
GLOBAL_CONFIG_HASHES = %w{ default_secrets_provider }.freeze
|
|
113
|
+
|
|
114
|
+
# Extract global (non-profile) settings from credentials file.
|
|
115
|
+
#
|
|
116
|
+
# @since 19.1
|
|
117
|
+
# @return [Hash]
|
|
118
|
+
def global_options
|
|
119
|
+
globals = credentials_config.filter { |_, v| v.is_a? String }
|
|
120
|
+
globals.merge! credentials_config.filter { |k, _| GLOBAL_CONFIG_HASHES.include? k }
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
SUPPORTED_SECRETS_PROVIDERS = %w{ hashicorp-vault }.freeze
|
|
124
|
+
|
|
125
|
+
# Resolve all secrets in a credentials file
|
|
126
|
+
#
|
|
127
|
+
# @since 19.1
|
|
128
|
+
# @param profile [String] Profile to resolve secrets in.
|
|
129
|
+
# @return [Hash]
|
|
130
|
+
def resolve_secrets(profile)
|
|
131
|
+
return unless credentials_config
|
|
132
|
+
raise NoCredentialsFound.new("No credentials found for profile '#{profile}'") unless credentials_config[profile]
|
|
133
|
+
|
|
134
|
+
secrets = credentials_config[profile].filter { |k, v| v.is_a?(Hash) && v.keys.include?("secret") }
|
|
135
|
+
return if secrets.empty?
|
|
136
|
+
|
|
137
|
+
secrets.each do |option, secrets_config|
|
|
138
|
+
unless valid_secrets_provider?(secrets_config)
|
|
139
|
+
raise UnsupportedSecretsProvider.new("Unsupported credentials secrets provider on '#{option}' for profile '#{profile}'")
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
secrets_config.merge!(default_secrets_provider)
|
|
143
|
+
|
|
144
|
+
logger.debug("Resolving credentials secret '#{option}' for profile '#{profile}'")
|
|
145
|
+
begin
|
|
146
|
+
resolved_value = resolve_secret(secrets_config)
|
|
147
|
+
ensure
|
|
148
|
+
raise UnresolvedSecret.new("Could not resolve secret '#{option}' for profile '#{profile}'") if resolved_value.nil?
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
credentials_config[profile][option] = resolved_value
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# Check, if referenced secrets provider is supported.
|
|
156
|
+
#
|
|
157
|
+
# @since 19.1
|
|
158
|
+
# @param secrets_config [Hash] Parsed contents of a secret in a profile.
|
|
159
|
+
# @return [true, false]
|
|
160
|
+
def valid_secrets_provider?(secrets_config)
|
|
161
|
+
provider_config = secrets_config["secrets_provider"] || default_secrets_provider
|
|
162
|
+
provider = provider_config["name"]
|
|
163
|
+
|
|
164
|
+
provider && SUPPORTED_SECRETS_PROVIDERS.include?(provider)
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def default_secrets_provider
|
|
168
|
+
global_options["default_secrets_provider"]
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
# Resolve a specific secret.
|
|
172
|
+
#
|
|
173
|
+
# To be replaced later by a Train-like framework to support multiple backends.
|
|
174
|
+
#
|
|
175
|
+
# @since 19.1
|
|
176
|
+
# @param secrets_config [Hash] Parsed contents of a secret in a profile.
|
|
177
|
+
# @return [String]
|
|
178
|
+
def resolve_secret(secrets_config)
|
|
179
|
+
resolve_secret_hashicorp(secrets_config)
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
# Resolver logic for Hashicorp Vault.
|
|
183
|
+
#
|
|
184
|
+
# Local lazy loading of Gems which are not part of chef-config or chef-utils,
|
|
185
|
+
# but chef itself to be switched by a unified secrets mechanism for credentials
|
|
186
|
+
# and Chef DSL later. Showstopper mitigation for 19 GA.
|
|
187
|
+
#
|
|
188
|
+
# @since 19.1
|
|
189
|
+
# @param secrets_config [Hash] Parsed contents of a secret in a profile.
|
|
190
|
+
# @return [String]
|
|
191
|
+
def resolve_secret_hashicorp(secrets_config)
|
|
192
|
+
vault_config = secrets_config.transform_keys(&:to_sym)
|
|
193
|
+
vault_config[:address] = vault_config[:endpoint]
|
|
194
|
+
|
|
195
|
+
# Lazy require due to Gem being part of Chef and rarely used functionality
|
|
196
|
+
require "vault" unless defined? Vault
|
|
197
|
+
@vault ||= Vault::Client.new(vault_config)
|
|
198
|
+
|
|
199
|
+
secret = secrets_config["secret"]
|
|
200
|
+
engine = vault_config[:engine] || "secret"
|
|
201
|
+
engine_type = vault_config[:engine_type] || "kv2"
|
|
202
|
+
secret_value = case engine_type
|
|
203
|
+
when "kv", "kv1"
|
|
204
|
+
@vault.logical.read("#{engine_type}/#{secret}")
|
|
205
|
+
when "kv2"
|
|
206
|
+
@vault.kv(engine).read(secret)&.data
|
|
207
|
+
else
|
|
208
|
+
raise UnsupportedSecretsProvider.new("No support for secrets engine #{engine_type}")
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
# Always JSON for Hashicorp Vault, but this is future compatible to other providers
|
|
212
|
+
if secret_value.is_a?(Hash)
|
|
213
|
+
require "jmespath" unless defined? ::JMESPath
|
|
214
|
+
::JMESPath.search(secrets_config["field"], secret_value)
|
|
215
|
+
else
|
|
216
|
+
secret_value
|
|
217
|
+
end
|
|
99
218
|
end
|
|
100
219
|
end
|
|
101
220
|
end
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#
|
|
2
|
-
# Copyright:: Copyright (c)
|
|
2
|
+
# Copyright:: Copyright (c) 2009-2026 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
|
|
3
3
|
# License:: Apache License, Version 2.0
|
|
4
4
|
#
|
|
5
5
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#
|
|
2
|
-
# Copyright:: Copyright (c)
|
|
2
|
+
# Copyright:: Copyright (c) 2009-2026 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
|
|
3
3
|
# License:: Apache License, Version 2.0
|
|
4
4
|
#
|
|
5
5
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# Author:: Bryan McLellan <btm@loftninjas.org>
|
|
2
|
-
# Copyright:: Copyright (c)
|
|
2
|
+
# Copyright:: Copyright (c) 2009-2026 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
|
|
3
3
|
# License:: Apache License, Version 2.0
|
|
4
4
|
#
|
|
5
5
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -36,15 +36,17 @@ module ChefConfig
|
|
|
36
36
|
#
|
|
37
37
|
def load_credentials(profile)
|
|
38
38
|
# Tomlrb.load_file returns a hash with keys as strings
|
|
39
|
-
|
|
40
|
-
if contains_split_fqdn?(
|
|
39
|
+
credentials_config = parse_credentials_file
|
|
40
|
+
if contains_split_fqdn?(credentials_config, profile)
|
|
41
41
|
logger.warn("Credentials file #{credentials_file_path} contains target '#{profile}' as a Hash, expected a string.")
|
|
42
42
|
logger.warn("Hostnames must be surrounded by single quotes, e.g. ['host.example.org']")
|
|
43
43
|
end
|
|
44
44
|
|
|
45
|
+
resolve_secrets(profile)
|
|
46
|
+
|
|
45
47
|
# host names must be specified in credentials file as ['foo.example.org'] with quotes
|
|
46
|
-
if !
|
|
47
|
-
|
|
48
|
+
if !credentials_config.nil? && !credentials_config[profile].nil?
|
|
49
|
+
credentials_config[profile].transform_keys(&:to_sym) # return symbolized keys to match Train.options()
|
|
48
50
|
else
|
|
49
51
|
nil
|
|
50
52
|
end
|
|
@@ -59,6 +61,8 @@ module ChefConfig
|
|
|
59
61
|
# This will be a common mistake so we should catch it
|
|
60
62
|
#
|
|
61
63
|
def contains_split_fqdn?(hash, fqdn)
|
|
64
|
+
return unless fqdn.include?(".")
|
|
65
|
+
|
|
62
66
|
fqdn.split(".").reduce(hash) do |h, k|
|
|
63
67
|
v = h[k]
|
|
64
68
|
if Hash === v
|
|
@@ -74,21 +78,25 @@ module ChefConfig
|
|
|
74
78
|
#
|
|
75
79
|
# Credentials file preference:
|
|
76
80
|
#
|
|
77
|
-
# 1)
|
|
78
|
-
# 2)
|
|
79
|
-
# 3)
|
|
81
|
+
# 1) environment variable CHEF_CREDENTIALS_FILE
|
|
82
|
+
# 2) target_mode.credentials_file
|
|
83
|
+
# 3) /etc/chef/TARGET_MODE_HOST/credentials
|
|
84
|
+
# 4) user configuration ($HOME/.chef/target_credentials)
|
|
80
85
|
#
|
|
81
86
|
def credentials_file_path
|
|
82
87
|
tm_config = config.target_mode
|
|
83
88
|
profile = tm_config.host
|
|
84
89
|
|
|
90
|
+
env_file = ENV["CHEF_CREDENTIALS_FILE"]
|
|
85
91
|
credentials_file =
|
|
86
|
-
if
|
|
92
|
+
if env_file && File.exist?(env_file)
|
|
93
|
+
env_file
|
|
94
|
+
elsif tm_config.credentials_file && File.exist?(tm_config.credentials_file)
|
|
87
95
|
tm_config.credentials_file
|
|
88
96
|
elsif File.exist?(config.platform_specific_path("#{ChefConfig::Config.etc_chef_dir}/#{profile}/credentials"))
|
|
89
97
|
config.platform_specific_path("#{ChefConfig::Config.etc_chef_dir}/#{profile}/credentials")
|
|
90
98
|
else
|
|
91
|
-
|
|
99
|
+
PathHelper.home(ChefUtils::Dist::Infra::USER_CONF_DIR, "target_credentials").freeze
|
|
92
100
|
end
|
|
93
101
|
|
|
94
102
|
raise ArgumentError, "No credentials file found for target '#{profile}'" unless credentials_file
|
|
@@ -112,7 +120,7 @@ module ChefConfig
|
|
|
112
120
|
# Load the credentials file, and place any valid settings into the train configuration
|
|
113
121
|
credentials = load_credentials(tm_config.host)
|
|
114
122
|
|
|
115
|
-
protocol = credentials
|
|
123
|
+
protocol = credentials&.dig(:transport_protocol) || tm_config.protocol
|
|
116
124
|
train_config = tm_config.to_hash.select { |k| Train.options(protocol).key?(k) }
|
|
117
125
|
logger.trace("Using target mode options from #{ChefUtils::Dist::Infra::PRODUCT} config file: #{train_config.keys.join(", ")}") if train_config
|
|
118
126
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#
|
|
2
2
|
# Author:: Bryan McLellan <btm@loftninjas.org>
|
|
3
|
-
# Copyright:: Copyright (c)
|
|
3
|
+
# Copyright:: Copyright (c) 2009-2026 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
|
|
4
4
|
# License:: Apache License, Version 2.0
|
|
5
5
|
#
|
|
6
6
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -57,8 +57,8 @@ module ChefConfig
|
|
|
57
57
|
|
|
58
58
|
def self.join(*args, windows: ChefUtils.windows?)
|
|
59
59
|
path_separator_regex = Regexp.escape(windows ? "#{File::SEPARATOR}#{BACKSLASH}" : File::SEPARATOR)
|
|
60
|
-
trailing_slashes_regex = /[#{path_separator_regex}]
|
|
61
|
-
leading_slashes_regex = /^[#{path_separator_regex}]
|
|
60
|
+
trailing_slashes_regex = /[#{path_separator_regex}]+$/
|
|
61
|
+
leading_slashes_regex = /^[#{path_separator_regex}]+/
|
|
62
62
|
separator = path_separator(windows: windows)
|
|
63
63
|
|
|
64
64
|
args.flatten!
|
|
@@ -152,11 +152,12 @@ module ChefConfig
|
|
|
152
152
|
path = Pathname.new(path).cleanpath.to_s
|
|
153
153
|
if windows
|
|
154
154
|
# ensure all forward slashes are backslashes
|
|
155
|
-
path.gsub(File::SEPARATOR, path_separator(windows: windows))
|
|
155
|
+
path.gsub!(File::SEPARATOR, path_separator(windows: windows))
|
|
156
156
|
else
|
|
157
157
|
# ensure all backslashes are forward slashes
|
|
158
|
-
path.gsub(BACKSLASH, File::SEPARATOR)
|
|
158
|
+
path.gsub!(BACKSLASH, File::SEPARATOR)
|
|
159
159
|
end
|
|
160
|
+
path
|
|
160
161
|
end
|
|
161
162
|
|
|
162
163
|
# This is not just escaping for something like use in Regexps, or in globs. For the former
|
data/lib/chef-config/version.rb
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright:: Copyright (c)
|
|
1
|
+
# Copyright:: Copyright (c) 2009-2026 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
|
|
2
2
|
# License:: Apache License, Version 2.0
|
|
3
3
|
#
|
|
4
4
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -15,5 +15,5 @@
|
|
|
15
15
|
|
|
16
16
|
module ChefConfig
|
|
17
17
|
CHEFCONFIG_ROOT = File.expand_path("..", __dir__)
|
|
18
|
-
VERSION = "
|
|
18
|
+
VERSION = "19.1.164".freeze
|
|
19
19
|
end
|
data/lib/chef-config/windows.rb
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#
|
|
2
|
-
# Copyright:: Copyright (c)
|
|
2
|
+
# Copyright:: Copyright (c) 2009-2026 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
|
|
3
3
|
# License:: Apache License, Version 2.0
|
|
4
4
|
#
|
|
5
5
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#
|
|
2
2
|
# Author:: Daniel DeLeo (<dan@chef.io>)
|
|
3
|
-
# Copyright:: Copyright (c)
|
|
3
|
+
# Copyright:: Copyright (c) 2009-2026 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
|
|
4
4
|
# License:: Apache License, Version 2.0
|
|
5
5
|
#
|
|
6
6
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
data/lib/chef-config.rb
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#
|
|
2
|
-
# Copyright:: Copyright (c)
|
|
2
|
+
# Copyright:: Copyright (c) 2009-2026 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
|
|
3
3
|
# License:: Apache License, Version 2.0
|
|
4
4
|
#
|
|
5
5
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
data/spec/unit/config_spec.rb
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#
|
|
2
2
|
# Author:: Adam Jacob (<adam@chef.io>)
|
|
3
3
|
# Author:: Kyle Goodwin (<kgoodwin@primerevenue.com>)
|
|
4
|
-
# Copyright:: Copyright (c)
|
|
4
|
+
# Copyright:: Copyright (c) 2009-2026 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
|
|
5
5
|
# License:: Apache License, Version 2.0
|
|
6
6
|
#
|
|
7
7
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -22,6 +22,8 @@ require "chef-config/config"
|
|
|
22
22
|
require "date" unless defined?(Date)
|
|
23
23
|
|
|
24
24
|
RSpec.describe ChefConfig::Config do
|
|
25
|
+
let(:target_mode_host) { "fluffy.kittens.org".freeze }
|
|
26
|
+
|
|
25
27
|
before(:each) do
|
|
26
28
|
ChefConfig::Config.reset
|
|
27
29
|
|
|
@@ -379,8 +381,6 @@ RSpec.describe ChefConfig::Config do
|
|
|
379
381
|
end
|
|
380
382
|
|
|
381
383
|
context "when target mode is enabled" do
|
|
382
|
-
let(:target_mode_host) { "fluffy.kittens.org" }
|
|
383
|
-
|
|
384
384
|
before do
|
|
385
385
|
ChefConfig::Config.target_mode.enabled = true
|
|
386
386
|
ChefConfig::Config.target_mode.host = target_mode_host
|
|
@@ -400,6 +400,25 @@ RSpec.describe ChefConfig::Config do
|
|
|
400
400
|
end
|
|
401
401
|
end
|
|
402
402
|
|
|
403
|
+
describe "ChefConfig::Config[:chef_guid_path]" do
|
|
404
|
+
it "sets the default path to the chef guid" do
|
|
405
|
+
expected_path = ChefConfig::PathHelper.join(ChefConfig::Config.config_dir, "chef_guid")
|
|
406
|
+
expect(ChefConfig::Config.chef_guid_path).to eq(expected_path)
|
|
407
|
+
end
|
|
408
|
+
|
|
409
|
+
context "when target mode is enabled" do
|
|
410
|
+
before do
|
|
411
|
+
ChefConfig::Config.target_mode.enabled = true
|
|
412
|
+
ChefConfig::Config.target_mode.host = target_mode_host
|
|
413
|
+
end
|
|
414
|
+
|
|
415
|
+
it "sets the default path to the chef guid with the target host name" do
|
|
416
|
+
expected_path = ChefConfig::PathHelper.join(ChefConfig::Config.config_dir, target_mode_host, "chef_guid")
|
|
417
|
+
expect(ChefConfig::Config.chef_guid_path).to eq(expected_path)
|
|
418
|
+
end
|
|
419
|
+
end
|
|
420
|
+
end
|
|
421
|
+
|
|
403
422
|
describe "ChefConfig::Config[:fips]" do
|
|
404
423
|
let(:fips_enabled) { false }
|
|
405
424
|
|
|
@@ -495,7 +514,6 @@ RSpec.describe ChefConfig::Config do
|
|
|
495
514
|
end
|
|
496
515
|
|
|
497
516
|
describe "ChefConfig::Config[:cache_path]" do
|
|
498
|
-
let(:target_mode_host) { "fluffy.kittens.org" }
|
|
499
517
|
let(:target_mode_primary_cache_path) { ChefUtils.windows? ? "#{primary_cache_path}\\#{target_mode_host}" : "#{primary_cache_path}/#{target_mode_host}" }
|
|
500
518
|
let(:target_mode_secondary_cache_path) { ChefUtils.windows? ? "#{secondary_cache_path}\\#{target_mode_host}" : "#{secondary_cache_path}/#{target_mode_host}" }
|
|
501
519
|
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright:: Copyright (c) 2009-2026 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
|
|
3
|
+
# License:: Apache License, Version 2.0
|
|
4
|
+
#
|
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
# you may not use this file except in compliance with the License.
|
|
7
|
+
# You may obtain a copy of the License at
|
|
8
|
+
#
|
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
#
|
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
# See the License for the specific language governing permissions and
|
|
15
|
+
# limitations under the License.
|
|
16
|
+
#
|
|
17
|
+
|
|
18
|
+
require "spec_helper"
|
|
19
|
+
require "chef-config/config"
|
|
20
|
+
require "chef-config/mixin/credentials"
|
|
21
|
+
|
|
22
|
+
module Vault
|
|
23
|
+
class Client; end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
RSpec.describe ChefConfig::Mixin::Credentials do
|
|
27
|
+
|
|
28
|
+
let(:test_class) { Class.new { include ChefConfig::Mixin::Credentials } }
|
|
29
|
+
subject(:test_obj) { test_class.new }
|
|
30
|
+
|
|
31
|
+
describe "#credentials_profile" do
|
|
32
|
+
context "when an explicit profile is given" do
|
|
33
|
+
it "passes it through" do
|
|
34
|
+
expect(test_obj.credentials_profile("webserver")).to eq("webserver")
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
context "when an environment variable is set" do
|
|
39
|
+
before(:all) do
|
|
40
|
+
@original_env = ENV.to_hash
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
after(:all) do
|
|
44
|
+
ENV.clear
|
|
45
|
+
ENV.update(@original_env)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
before(:each) do
|
|
49
|
+
ENV["CHEF_PROFILE"] = "acme-server"
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
it "picks the profile correctly" do
|
|
53
|
+
expect(test_obj.credentials_profile).to eq("acme-server")
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
context "when no profile is given" do
|
|
58
|
+
it "picks the default one" do
|
|
59
|
+
expect(test_obj.credentials_profile).to eq("default")
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
describe "#resolve_secrets" do
|
|
65
|
+
context "when no credentials were loaded" do
|
|
66
|
+
it "returns" do
|
|
67
|
+
allow(test_obj).to receive(:credentials_config).and_return(nil)
|
|
68
|
+
expect(test_obj.resolve_secrets("dummy")).to eq(nil)
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
context "when credentials do not contain specified profile" do
|
|
73
|
+
it "raises an error" do
|
|
74
|
+
allow(test_obj).to receive(:credentials_config).and_return({ "webserver" => {} })
|
|
75
|
+
expect { test_obj.resolve_secrets("dummy") }.to raise_error(ChefConfig::NoCredentialsFound, /No credentials found for profile/)
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
context "when no secrets were referenced in profile" do
|
|
80
|
+
it "returns" do
|
|
81
|
+
allow(test_obj).to receive(:credentials_config).and_return({ "webserver2" => {} })
|
|
82
|
+
expect(test_obj.resolve_secrets("webserver2")).to eq(nil)
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
describe "#valid_secrets_provider?" do
|
|
88
|
+
context "when global, valid configuration was provided" do
|
|
89
|
+
let(:global_options) { { "default_secrets_provider" => { "name" => "hashicorp-vault", "endpoint" => "https://198.51.100.5:8200", "token" => "hvs.1234567890" } } }
|
|
90
|
+
let(:secrets_config) { { "secret" => "/chef/sudo_password", "field" => "password" } }
|
|
91
|
+
|
|
92
|
+
it "returns true" do
|
|
93
|
+
allow(test_obj).to receive(:global_options).and_return(global_options)
|
|
94
|
+
expect(test_obj.valid_secrets_provider?(secrets_config)).to be(true)
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
context "when global, invalid configuration was provided" do
|
|
99
|
+
let(:global_options) { { "default_secrets_provider" => { "name" => "hashicorp-consul" } } }
|
|
100
|
+
let(:secrets_config) { { "secret" => "/chef/sudo_password", "field" => "password" } }
|
|
101
|
+
|
|
102
|
+
it "returns false" do
|
|
103
|
+
allow(test_obj).to receive(:global_options).and_return(global_options)
|
|
104
|
+
expect(test_obj.valid_secrets_provider?(secrets_config)).to be(false)
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
context "when global, invalid configuration is overridden with a correct value" do
|
|
109
|
+
let(:global_options) { { "default_secrets_provider" => { "name" => "hashicorp-consul" } } }
|
|
110
|
+
let(:secrets_config) { { "secrets_provider" => { "name" => "hashicorp-vault" }, "secret" => "/chef/sudo_password", "field" => "password" } }
|
|
111
|
+
|
|
112
|
+
it "returns false" do
|
|
113
|
+
allow(test_obj).to receive(:global_options).and_return(global_options)
|
|
114
|
+
expect(test_obj.valid_secrets_provider?(secrets_config)).to be(true)
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
describe "#resolve_secret" do
|
|
120
|
+
before do
|
|
121
|
+
allow(test_obj).to receive(:global_options).and_return(global_options)
|
|
122
|
+
|
|
123
|
+
# Simulate "vault" gem
|
|
124
|
+
allow(test_obj).to receive(:require).with("vault")
|
|
125
|
+
vault_double = double("Vault::Client")
|
|
126
|
+
allow(vault_double).to receive_message_chain("logical.read") { secrets_result }
|
|
127
|
+
allow(vault_double).to receive_message_chain("kv.read.data") { secrets_result }
|
|
128
|
+
test_obj.instance_variable_set(:@vault, vault_double)
|
|
129
|
+
|
|
130
|
+
# Simulate "jmespath" gem
|
|
131
|
+
allow(test_obj).to receive(:require).with("jmespath")
|
|
132
|
+
jmespath_double = double("JMESPath")
|
|
133
|
+
allow(jmespath_double).to receive_message_chain("search") { search_for = secrets_config["field"]; secrets_result[search_for] }
|
|
134
|
+
stub_const("::JMESPath", jmespath_double)
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
context "without default secrets provider being set" do
|
|
138
|
+
let(:global_options) {}
|
|
139
|
+
|
|
140
|
+
context "for a secret of type string" do
|
|
141
|
+
let(:secrets_result) { "secret" }
|
|
142
|
+
let(:secrets_config) { { "secrets_provider" => { "name" => "hashicorp-vault" }, "secret" => "/chef/sudo_password" } }
|
|
143
|
+
|
|
144
|
+
it "returns the complete value" do
|
|
145
|
+
expect(test_obj.resolve_secret(secrets_config)).to eq("secret")
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
context "for a secret of type hash" do
|
|
150
|
+
let(:secrets_result) { { "password" => "secret" } }
|
|
151
|
+
let(:secrets_config) { { "secrets_provider" => { "name" => "hashicorp-vault", "endpoint" => "https://198.51.100.5:8200", "token" => "hvs.1234567890" }, "secret" => "/chef/sudo_password", "field" => "password" } }
|
|
152
|
+
|
|
153
|
+
it "returns the correct subkey" do
|
|
154
|
+
expect(test_obj.resolve_secret(secrets_config)).to eq("secret")
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
context "with default secrets provider being set" do
|
|
160
|
+
let(:global_options) { { "default_secrets_provider" => { "name" => "hashicorp-vault", "endpoint" => "https://198.51.100.5:8200", "token" => "hvs.1234567890" } } }
|
|
161
|
+
|
|
162
|
+
context "for a secret of type string" do
|
|
163
|
+
let(:secrets_result) { "secret" }
|
|
164
|
+
let(:secrets_config) { { "secret" => "/chef/sudo_password" } }
|
|
165
|
+
|
|
166
|
+
it "returns the complete value" do
|
|
167
|
+
expect(test_obj.resolve_secret(secrets_config)).to eq("secret")
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
context "for a secret of type hash" do
|
|
172
|
+
let(:secrets_result) { { "password" => "secret" } }
|
|
173
|
+
let(:secrets_config) { { "secret" => "/chef/sudo_password", "field" => "password" } }
|
|
174
|
+
|
|
175
|
+
it "returns the correct subkey" do
|
|
176
|
+
expect(test_obj.resolve_secret(secrets_config)).to eq("secret")
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
end
|
data/spec/unit/fips_spec.rb
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#
|
|
2
2
|
# Author:: Matt Wrock (<matt@mattwrock.com>)
|
|
3
|
-
# Copyright:: Copyright (c)
|
|
3
|
+
# Copyright:: Copyright (c) 2009-2026 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
|
|
4
4
|
# License:: Apache License, Version 2.0
|
|
5
5
|
#
|
|
6
6
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#
|
|
2
2
|
# Author:: Bryan McLellan <btm@loftninjas.org>
|
|
3
|
-
# Copyright:: Copyright (c)
|
|
3
|
+
# Copyright:: Copyright (c) 2009-2026 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
|
|
4
4
|
# License:: Apache License, Version 2.0
|
|
5
5
|
#
|
|
6
6
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#
|
|
2
2
|
# Author:: Daniel DeLeo (<dan@chef.io>)
|
|
3
|
-
# Copyright:: Copyright (c)
|
|
3
|
+
# Copyright:: Copyright (c) 2009-2026 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
|
|
4
4
|
# License:: Apache License, Version 2.0
|
|
5
5
|
#
|
|
6
6
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
metadata
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: chef-config
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 19.1.164
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Adam Jacob
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: bin
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
13
12
|
- !ruby/object:Gem::Dependency
|
|
14
13
|
name: chef-utils
|
|
@@ -16,14 +15,14 @@ dependencies:
|
|
|
16
15
|
requirements:
|
|
17
16
|
- - '='
|
|
18
17
|
- !ruby/object:Gem::Version
|
|
19
|
-
version:
|
|
18
|
+
version: 19.1.164
|
|
20
19
|
type: :runtime
|
|
21
20
|
prerelease: false
|
|
22
21
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
22
|
requirements:
|
|
24
23
|
- - '='
|
|
25
24
|
- !ruby/object:Gem::Version
|
|
26
|
-
version:
|
|
25
|
+
version: 19.1.164
|
|
27
26
|
- !ruby/object:Gem::Dependency
|
|
28
27
|
name: mixlib-shellout
|
|
29
28
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -106,7 +105,20 @@ dependencies:
|
|
|
106
105
|
- - "~>"
|
|
107
106
|
- !ruby/object:Gem::Version
|
|
108
107
|
version: '1.2'
|
|
109
|
-
|
|
108
|
+
- !ruby/object:Gem::Dependency
|
|
109
|
+
name: racc
|
|
110
|
+
requirement: !ruby/object:Gem::Requirement
|
|
111
|
+
requirements:
|
|
112
|
+
- - ">="
|
|
113
|
+
- !ruby/object:Gem::Version
|
|
114
|
+
version: '0'
|
|
115
|
+
type: :runtime
|
|
116
|
+
prerelease: false
|
|
117
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
118
|
+
requirements:
|
|
119
|
+
- - ">="
|
|
120
|
+
- !ruby/object:Gem::Version
|
|
121
|
+
version: '0'
|
|
110
122
|
email:
|
|
111
123
|
- adam@chef.io
|
|
112
124
|
executables: []
|
|
@@ -131,6 +143,7 @@ files:
|
|
|
131
143
|
- lib/chef-config/workstation_config_loader.rb
|
|
132
144
|
- spec/spec_helper.rb
|
|
133
145
|
- spec/unit/config_spec.rb
|
|
146
|
+
- spec/unit/credentials_spec.rb
|
|
134
147
|
- spec/unit/fips_spec.rb
|
|
135
148
|
- spec/unit/path_helper_spec.rb
|
|
136
149
|
- spec/unit/workstation_config_loader_spec.rb
|
|
@@ -143,7 +156,6 @@ metadata:
|
|
|
143
156
|
documentation_uri: https://github.com/chef/chef/tree/main/chef-config/README.md
|
|
144
157
|
homepage_uri: https://github.com/chef/chef/tree/main/chef-config
|
|
145
158
|
source_code_uri: https://github.com/chef/chef/tree/main/chef-config
|
|
146
|
-
post_install_message:
|
|
147
159
|
rdoc_options: []
|
|
148
160
|
require_paths:
|
|
149
161
|
- lib
|
|
@@ -158,8 +170,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
158
170
|
- !ruby/object:Gem::Version
|
|
159
171
|
version: '0'
|
|
160
172
|
requirements: []
|
|
161
|
-
rubygems_version: 3.
|
|
162
|
-
signing_key:
|
|
173
|
+
rubygems_version: 3.6.9
|
|
163
174
|
specification_version: 4
|
|
164
175
|
summary: Chef Infra's default configuration and config loading library
|
|
165
176
|
test_files: []
|