chef 18.10.17 → 18.11.11
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/Gemfile +15 -7
- data/README.md +0 -1
- data/chef-universal-mingw-ucrt.gemspec +4 -2
- data/chef.gemspec +15 -12
- data/lib/chef/compliance/runner.rb +19 -1
- data/lib/chef/dsl/rest_resource.rb +223 -16
- data/lib/chef/formatters/error_description.rb +8 -1
- data/lib/chef/http/authenticator.rb +17 -4
- data/lib/chef/mixin/template.rb +24 -2
- data/lib/chef/monkey_patches/reline-windows.rb +116 -0
- data/lib/chef/provider/file/content.rb +3 -2
- data/lib/chef/provider/file.rb +1 -1
- data/lib/chef/provider/ifconfig/redhat.rb +1 -1
- data/lib/chef/provider/package/dnf/dnf_helper.py +355 -65
- data/lib/chef/provider/package/dnf/python_helper.rb +7 -4
- data/lib/chef/provider/package/dnf.rb +25 -6
- data/lib/chef/provider/package/yum.rb +1 -1
- data/lib/chef/resource/_rest_resource.rb +4 -2
- data/lib/chef/resource/archive_file.rb +52 -18
- data/lib/chef/resource/chef_client_config.rb +185 -19
- data/lib/chef/resource/chef_vault_secret.rb +1 -1
- data/lib/chef/resource/chocolatey_installer.rb +31 -13
- data/lib/chef/resource/chocolatey_package.rb +1 -1
- data/lib/chef/resource/habitat/habitat_package.rb +1 -1
- data/lib/chef/resource/habitat/habitat_sup.rb +2 -2
- data/lib/chef/resource/habitat_config.rb +1 -1
- data/lib/chef/resource/habitat_service.rb +1 -1
- data/lib/chef/resource/openssl_ec_private_key.rb +1 -1
- data/lib/chef/resource/openssl_ec_public_key.rb +2 -2
- data/lib/chef/resource/openssl_rsa_private_key.rb +1 -1
- data/lib/chef/resource/openssl_rsa_public_key.rb +2 -2
- data/lib/chef/resource/openssl_x509_certificate.rb +1 -1
- data/lib/chef/resource/openssl_x509_request.rb +1 -1
- data/lib/chef/resource/powershell_package_source.rb +1 -1
- data/lib/chef/resource/rhsm_register.rb +1 -1
- data/lib/chef/resource/windows_ad_join.rb +1 -1
- data/lib/chef/resource/windows_certificate.rb +1 -1
- data/lib/chef/resource/windows_service.rb +1 -1
- data/lib/chef/resource/windows_task.rb +1 -1
- data/lib/chef/resource/yum_package.rb +1 -1
- data/lib/chef/resource/yum_repository.rb +2 -2
- data/lib/chef/shell.rb +7 -0
- data/lib/chef/version.rb +1 -1
- metadata +59 -34
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
1
2
|
#
|
|
2
|
-
# Copyright:: Copyright (c)
|
|
3
|
+
# Copyright:: Copyright (c) 2009-2026 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
|
|
4
|
+
# Copyright:: Copyright (c) 2026 Meta Platforms, Inc.
|
|
5
|
+
# Copyright:: Copyright (c) 2026 Phil Dibowitz
|
|
3
6
|
# License:: Apache License, Version 2.0
|
|
4
7
|
#
|
|
5
8
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -41,10 +44,10 @@ class Chef
|
|
|
41
44
|
|
|
42
45
|
def dnf_command
|
|
43
46
|
# platform-python is used for system tools on RHEL 8 and is installed under /usr/libexec
|
|
47
|
+
py_cmd = "try:\n import libdnf5\nexcept ImportError:\n import dnf"
|
|
44
48
|
@dnf_command ||= begin
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
end
|
|
49
|
+
executables = where("platform-python", "python", "python3", "python2", "python2.7", extra_path: "/usr/libexec")
|
|
50
|
+
cmd = executables.find { |f| shell_out("#{f} -c '#{py_cmd}'").exitstatus == 0 }
|
|
48
51
|
raise Chef::Exceptions::Package, "cannot find dnf libraries, you may need to use yum_package" unless cmd
|
|
49
52
|
|
|
50
53
|
"#{cmd} #{DNF_HELPER}"
|
|
@@ -1,5 +1,7 @@
|
|
|
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
|
+
# Copyright:: Copyright (c) 2026 Meta Platforms, Inc.
|
|
4
|
+
# Copyright:: Copyright (c) 2026 Phil Dibowitz
|
|
3
5
|
# License:: Apache License, Version 2.0
|
|
4
6
|
#
|
|
5
7
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -62,6 +64,13 @@ class Chef
|
|
|
62
64
|
@python_helper ||= PythonHelper.instance
|
|
63
65
|
end
|
|
64
66
|
|
|
67
|
+
def dnf5?
|
|
68
|
+
@dnf5 ||= begin
|
|
69
|
+
dnf_version = shell_out!("dnf --version").stdout
|
|
70
|
+
dnf_version =~ /dnf5/i
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
65
74
|
def load_current_resource
|
|
66
75
|
flushcache if new_resource.flush_cache[:before]
|
|
67
76
|
|
|
@@ -138,14 +147,19 @@ class Chef
|
|
|
138
147
|
# NB: the dnf_package provider manages individual single packages, please do not submit issues or PRs to try to add wildcard
|
|
139
148
|
# support to lock / unlock. The best solution is to write an execute resource which does a not_if `dnf versionlock | grep '^pattern`` kind of approach
|
|
140
149
|
def lock_package(names, versions)
|
|
141
|
-
|
|
150
|
+
default_opts = dnf5? ? [] : %w{-d0 -e0}
|
|
151
|
+
dnf(default_opts, options, "versionlock", "add", resolved_package_lock_names(names))
|
|
142
152
|
end
|
|
143
153
|
|
|
144
154
|
# NB: the dnf_package provider manages individual single packages, please do not submit issues or PRs to try to add wildcard
|
|
145
155
|
# support to lock / unlock. The best solution is to write an execute resource which does a only_if `dnf versionlock | grep '^pattern`` kind of approach
|
|
146
156
|
def unlock_package(names, versions)
|
|
147
|
-
|
|
148
|
-
|
|
157
|
+
if dnf5?
|
|
158
|
+
dnf("-y", options, "versionlock", "delete", resolved_package_lock_names(names))
|
|
159
|
+
else
|
|
160
|
+
# dnf versionlock delete on rhel6 needs the glob nonsense in the following command
|
|
161
|
+
dnf("-d0", "-e0", "-y", options, "versionlock", "delete", resolved_package_lock_names(names).map { |n| "*:#{n}-*" })
|
|
162
|
+
end
|
|
149
163
|
end
|
|
150
164
|
|
|
151
165
|
private
|
|
@@ -167,8 +181,13 @@ class Chef
|
|
|
167
181
|
@locked_packages ||=
|
|
168
182
|
begin
|
|
169
183
|
locked = dnf("versionlock", "list")
|
|
170
|
-
|
|
171
|
-
|
|
184
|
+
if dnf5?
|
|
185
|
+
locked.stdout.each_line.select { |x| x.start_with?("Package name:") }
|
|
186
|
+
.map { |line| line.split(": ").last.strip }
|
|
187
|
+
else
|
|
188
|
+
locked.stdout.each_line.map do |line|
|
|
189
|
+
line.sub(/-[^-]*-[^-]*$/, "").split(":").last.strip
|
|
190
|
+
end
|
|
172
191
|
end
|
|
173
192
|
end
|
|
174
193
|
end
|
|
@@ -174,13 +174,15 @@ action_class do
|
|
|
174
174
|
end
|
|
175
175
|
|
|
176
176
|
def rest_url_collection
|
|
177
|
-
current_resource.class.
|
|
177
|
+
endpoint = current_resource.class.rest_api_endpoint || ""
|
|
178
|
+
"#{endpoint}#{current_resource.class.rest_api_collection}"
|
|
178
179
|
end
|
|
179
180
|
|
|
180
181
|
# Resource document URL after RFC 6570 template evaluation via properties substitution
|
|
181
182
|
def rest_url_document
|
|
183
|
+
endpoint = current_resource.class.rest_api_endpoint || ""
|
|
182
184
|
template = ::Addressable::Template.new(current_resource.class.rest_api_document)
|
|
183
|
-
template.expand(property_map)
|
|
185
|
+
"#{endpoint}#{template.expand(property_map)}"
|
|
184
186
|
end
|
|
185
187
|
|
|
186
188
|
# Convenience method for conditional requires
|
|
@@ -172,9 +172,16 @@ class Chef
|
|
|
172
172
|
|
|
173
173
|
if new_resource.owner || new_resource.group
|
|
174
174
|
converge_by("set owner of files extracted in #{new_resource.destination} to #{new_resource.owner}:#{new_resource.group}") do
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
175
|
+
dest_realpath = ::File.expand_path(new_resource.destination)
|
|
176
|
+
Archive::Reader.open_filename(new_resource.path, nil, strip_components: new_resource.strip_components) do |archive|
|
|
177
|
+
archive.each_entry do |e|
|
|
178
|
+
# Validate path is within destination before chown-ing.
|
|
179
|
+
# Use expand_path for security check, original string for the chown call.
|
|
180
|
+
chown_path_expanded = ::File.expand_path("#{new_resource.destination}/#{e.pathname}")
|
|
181
|
+
next unless chown_path_expanded.start_with?(dest_realpath + ::File::SEPARATOR) || chown_path_expanded == dest_realpath
|
|
182
|
+
|
|
183
|
+
FileUtils.chown(new_resource.owner, new_resource.group, "#{new_resource.destination}/#{e.pathname}")
|
|
184
|
+
end
|
|
178
185
|
end
|
|
179
186
|
end
|
|
180
187
|
end
|
|
@@ -211,16 +218,23 @@ class Chef
|
|
|
211
218
|
# @return [Boolean]
|
|
212
219
|
def archive_differs_from_disk?(src, dest)
|
|
213
220
|
modified = false
|
|
214
|
-
|
|
221
|
+
dest_realpath = ::File.expand_path(dest)
|
|
215
222
|
Chef::Log.trace("Beginning the comparison of file mtime between contents of #{src} and #{dest}")
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
223
|
+
Archive::Reader.open_filename(src, nil, strip_components: new_resource.strip_components) do |archive|
|
|
224
|
+
archive.each_entry do |e|
|
|
225
|
+
pathname = ::File.expand_path(e.pathname, dest_realpath)
|
|
226
|
+
# Skip archive entries that resolve outside the destination directory.
|
|
227
|
+
# A traversal entry (e.g. "../escaped") must not influence the mtime
|
|
228
|
+
# comparison and must never trigger extraction.
|
|
229
|
+
next unless pathname.start_with?(dest_realpath + ::File::SEPARATOR) || pathname == dest_realpath
|
|
230
|
+
|
|
231
|
+
if ::File.exist?(pathname)
|
|
232
|
+
Chef::Log.trace("#{pathname} mtime is #{::File.mtime(pathname)} and archive is #{e.mtime}")
|
|
233
|
+
modified = true unless ::File.mtime(pathname) == e.mtime
|
|
234
|
+
else
|
|
235
|
+
Chef::Log.trace("#{pathname} doesn't exist on disk, but exists in the archive")
|
|
236
|
+
modified = true
|
|
237
|
+
end
|
|
224
238
|
end
|
|
225
239
|
end
|
|
226
240
|
modified
|
|
@@ -235,15 +249,35 @@ class Chef
|
|
|
235
249
|
# @return [void]
|
|
236
250
|
def extract(src, dest, options = [])
|
|
237
251
|
converge_by("extract #{src} to #{dest}") do
|
|
238
|
-
|
|
252
|
+
user_flags = [options].flatten.map { |option| extract_option_map[option] }.compact.reduce(:|).to_i
|
|
253
|
+
# Always apply libarchive security flags to prevent path traversal.
|
|
254
|
+
# These are not user-overridable hence directly added here
|
|
255
|
+
# instead of options array. NODOTDOT blocks "../" sequences,
|
|
256
|
+
# NOABSOLUTEPATHS blocks absolute-path entries, SECURE_SYMLINKS prevents
|
|
257
|
+
# symlink attacks. Combined with the check below in archive.each_entry
|
|
258
|
+
# they safeguard against Zip Slip / CWE-22 attacks.
|
|
259
|
+
security_flags = Archive::EXTRACT_SECURE_NODOTDOT |
|
|
260
|
+
Archive::EXTRACT_SECURE_NOABSOLUTEPATHS |
|
|
261
|
+
Archive::EXTRACT_SECURE_SYMLINKS
|
|
262
|
+
all_flags = user_flags | security_flags
|
|
263
|
+
dest_realpath = ::File.expand_path(dest)
|
|
239
264
|
|
|
240
265
|
Dir.chdir(dest) do
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
266
|
+
Archive::Reader.open_filename(src, nil, strip_components: new_resource.strip_components) do |archive|
|
|
267
|
+
archive.each_entry do |e|
|
|
268
|
+
# reject any entry whose resolved path
|
|
269
|
+
# falls outside the destination directory before calling into libarchive.
|
|
270
|
+
entry_realpath = ::File.expand_path(e.pathname, dest_realpath)
|
|
271
|
+
unless entry_realpath.start_with?(dest_realpath + ::File::SEPARATOR) || entry_realpath == dest_realpath
|
|
272
|
+
raise Chef::Exceptions::ValidationFailed,
|
|
273
|
+
"Refusing to extract archive entry #{e.pathname.inspect}: resolved path " \
|
|
274
|
+
"#{entry_realpath.inspect} is outside destination #{dest_realpath.inspect}. " \
|
|
275
|
+
"This archive may contain a path traversal attack."
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
archive.extract(e, all_flags)
|
|
279
|
+
end
|
|
245
280
|
end
|
|
246
|
-
archive.close
|
|
247
281
|
end
|
|
248
282
|
end
|
|
249
283
|
end
|
|
@@ -23,6 +23,14 @@ class Chef
|
|
|
23
23
|
|
|
24
24
|
provides :chef_client_config
|
|
25
25
|
|
|
26
|
+
DIRECTORY_SPEC_DEFAULTS = {
|
|
27
|
+
config: { mode: "0750", inherits: :auto },
|
|
28
|
+
client_d: { mode: "0750", inherits: :auto },
|
|
29
|
+
logs: { mode: "0755", inherits: :auto },
|
|
30
|
+
cache: { mode: "0750", inherits: :auto },
|
|
31
|
+
backups: { mode: "0750", inherits: :auto },
|
|
32
|
+
}.freeze
|
|
33
|
+
|
|
26
34
|
description "Use the **chef_client_config** resource to create a client.rb file in the #{ChefUtils::Dist::Infra::PRODUCT} configuration directory. See the [client.rb docs](https://docs.chef.io/config_rb_client/) for more details on options available in the client.rb configuration file."
|
|
27
35
|
introduced "16.6"
|
|
28
36
|
examples <<~DOC
|
|
@@ -76,12 +84,12 @@ class Chef
|
|
|
76
84
|
chef_server_url 'https://chef.example.dmz'
|
|
77
85
|
report_handlers [
|
|
78
86
|
{
|
|
79
|
-
|
|
80
|
-
|
|
87
|
+
'class' => 'ReportHandler1Class',
|
|
88
|
+
'arguments' => ["'FirstArgument'", "'SecondArgument'"],
|
|
81
89
|
},
|
|
82
90
|
{
|
|
83
|
-
|
|
84
|
-
|
|
91
|
+
'class' => 'ReportHandler2Class',
|
|
92
|
+
'arguments' => ["'FirstArgument'", "'SecondArgument'"],
|
|
85
93
|
},
|
|
86
94
|
]
|
|
87
95
|
end
|
|
@@ -96,6 +104,48 @@ class Chef
|
|
|
96
104
|
data_collector_token 'TEST_TOKEN_TEST'
|
|
97
105
|
end
|
|
98
106
|
```
|
|
107
|
+
|
|
108
|
+
**Set Linux directory ownership and modes**:
|
|
109
|
+
|
|
110
|
+
```ruby
|
|
111
|
+
chef_client_config 'Create client.rb' do
|
|
112
|
+
chef_server_url 'https://chef.example.dmz'
|
|
113
|
+
user 'root'
|
|
114
|
+
group 'root'
|
|
115
|
+
file_cache_path '/var/chef/cache'
|
|
116
|
+
file_backup_path '/var/chef/backups'
|
|
117
|
+
log_location '/var/log/chef/client.log'
|
|
118
|
+
directory_specs(
|
|
119
|
+
config: { owner: 'root', group: 'root', mode: '0700' },
|
|
120
|
+
client_d: { owner: 'root', group: 'root', mode: '0700' },
|
|
121
|
+
logs: { owner: 'root', group: 'root', mode: '0750' },
|
|
122
|
+
cache: { owner: 'root', group: 'root', mode: '0750' },
|
|
123
|
+
backups: { owner: 'root', group: 'root', mode: '0750' }
|
|
124
|
+
)
|
|
125
|
+
end
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
**Set Windows directory inheritance and rights**:
|
|
129
|
+
|
|
130
|
+
```ruby
|
|
131
|
+
chef_client_config 'Create client.rb' do
|
|
132
|
+
chef_server_url 'https://chef.example.dmz'
|
|
133
|
+
config_directory 'C:/chef'
|
|
134
|
+
file_cache_path 'C:/chef/cache'
|
|
135
|
+
file_backup_path 'C:/chef/backups'
|
|
136
|
+
log_location 'C:/chef/log/client.log'
|
|
137
|
+
directory_specs(
|
|
138
|
+
config: {
|
|
139
|
+
owner: 'Administrators',
|
|
140
|
+
inherits: false,
|
|
141
|
+
rights: {
|
|
142
|
+
'Administrators' => :full_control,
|
|
143
|
+
'SYSTEM' => :full_control,
|
|
144
|
+
},
|
|
145
|
+
}
|
|
146
|
+
)
|
|
147
|
+
end
|
|
148
|
+
```
|
|
99
149
|
DOC
|
|
100
150
|
|
|
101
151
|
# @todo policy_file or policy_group being set requires the other to be set so enforce that.
|
|
@@ -254,21 +304,83 @@ class Chef
|
|
|
254
304
|
description: "The data collector token to interact with the data collector server URL (Automate). Note: If possible, use Chef Infra Server to do all data collection reporting, as this removes the need to distribute tokens to individual nodes.",
|
|
255
305
|
introduced: "17.8"
|
|
256
306
|
|
|
307
|
+
property :directory_specs, Hash,
|
|
308
|
+
description: <<~DESC,
|
|
309
|
+
Permission overrides for Chef-managed directories.
|
|
310
|
+
This must be a hash of hashes: top-level keys are directory names, values are per-directory option hashes.
|
|
311
|
+
|
|
312
|
+
Use this property like:
|
|
313
|
+
directory_specs(
|
|
314
|
+
config: { owner: "root", group: "root", mode: "0700" },
|
|
315
|
+
client_d: { owner: "root", group: "root", mode: "0700" },
|
|
316
|
+
logs: { owner: "root", group: "root", mode: "0700" },
|
|
317
|
+
cache: { owner: "root", group: "root", mode: "0700" },
|
|
318
|
+
backups: { owner: "root", group: "root", mode: "0700" }
|
|
319
|
+
)
|
|
320
|
+
|
|
321
|
+
Directory keys: :config, :client_d, :logs, :cache, :backups
|
|
322
|
+
Override keys: :owner, :group, :mode, :rights, :inherits (:rights and :inherits are Windows-only)
|
|
323
|
+
DESC
|
|
324
|
+
callbacks: {
|
|
325
|
+
"keys must be one of :config, :client_d, :logs, :cache, :backups" => lambda { |v|
|
|
326
|
+
valid_keys = %i{config client_d logs cache backups}
|
|
327
|
+
v.keys.all? { |k| valid_keys.include?(k) }
|
|
328
|
+
},
|
|
329
|
+
"values must be a hash with keys only from :owner, :group, :mode, :rights, :inherits" => lambda { |v|
|
|
330
|
+
valid_inner_keys = %i{owner group mode rights inherits}
|
|
331
|
+
v.values.all? { |value| value.is_a?(Hash) && value.keys.all? { |k| valid_inner_keys.include?(k) } }
|
|
332
|
+
},
|
|
333
|
+
},
|
|
334
|
+
default: DIRECTORY_SPEC_DEFAULTS,
|
|
335
|
+
coerce: proc { |v|
|
|
336
|
+
DIRECTORY_SPEC_DEFAULTS.merge(v) do |_key, base, override|
|
|
337
|
+
override.is_a?(Hash) ? base.merge(override) : override
|
|
338
|
+
end
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
property :client_rb_mode, String,
|
|
342
|
+
description: "The mode to set on the client.rb file that is written out by this resource (e.g., 0600).",
|
|
343
|
+
default: "0640"
|
|
344
|
+
|
|
257
345
|
action :create, description: "Create a client.rb config file and folders for configuring #{ChefUtils::Dist::Infra::PRODUCT}." do
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
346
|
+
# The logs directory is derived from log_location, which may be a symbol
|
|
347
|
+
# or a stream name (STDOUT/STDERR) rather than a file path. Only raise if
|
|
348
|
+
# the user customized logs specs but log_location is not a usable file path.
|
|
349
|
+
if new_resource.directory_specs[:logs] != DIRECTORY_SPEC_DEFAULTS[:logs] && log_directory.nil?
|
|
350
|
+
raise ArgumentError, "Invalid directory_specs: logs requires :logs (file path)"
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
# Build path map for each managed directory. For cache and backups, fall
|
|
354
|
+
# back to ChefConfig defaults when not explicitly set so that directory_specs
|
|
355
|
+
# overrides are applied even if the user didn't configure a custom path in client.rb.
|
|
356
|
+
spec_paths = {
|
|
357
|
+
config: new_resource.config_directory,
|
|
358
|
+
client_d: ::File.join(new_resource.config_directory, "client.d"),
|
|
359
|
+
logs: log_directory,
|
|
360
|
+
cache: new_resource.file_cache_path || ChefConfig::Config.file_cache_path,
|
|
361
|
+
backups: new_resource.file_backup_path || ChefConfig::Config.file_backup_path,
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
# Build directory specs based on user input and defaults
|
|
365
|
+
specs = {}
|
|
366
|
+
new_resource.directory_specs.each do |key, overrides|
|
|
367
|
+
path = spec_paths[key]
|
|
368
|
+
next if path.nil?
|
|
369
|
+
|
|
370
|
+
specs[key] = DirectorySpec.new(
|
|
371
|
+
path: path,
|
|
372
|
+
owner: overrides[:owner] || new_resource.user,
|
|
373
|
+
group: overrides[:group] || new_resource.group,
|
|
374
|
+
mode: overrides[:mode],
|
|
375
|
+
rights: overrides[:rights],
|
|
376
|
+
inherits: overrides[:inherits]
|
|
377
|
+
)
|
|
378
|
+
end
|
|
379
|
+
|
|
380
|
+
# Converge directories
|
|
381
|
+
specs.each_value do |spec|
|
|
382
|
+
directory spec.path do
|
|
383
|
+
spec.apply(self)
|
|
272
384
|
end
|
|
273
385
|
end
|
|
274
386
|
|
|
@@ -309,7 +421,7 @@ class Chef
|
|
|
309
421
|
data_collector_server_url: new_resource.data_collector_server_url,
|
|
310
422
|
data_collector_token: new_resource.data_collector_token
|
|
311
423
|
)
|
|
312
|
-
mode
|
|
424
|
+
mode new_resource.client_rb_mode
|
|
313
425
|
action :create
|
|
314
426
|
end
|
|
315
427
|
end
|
|
@@ -333,6 +445,60 @@ class Chef
|
|
|
333
445
|
"#{handler["class"]}.new(#{handler["arguments"].join(",")})"
|
|
334
446
|
end
|
|
335
447
|
end
|
|
448
|
+
|
|
449
|
+
# A helper class to encapsulate directory spec data and application logic for both *nix and Windows.
|
|
450
|
+
# This allows us to keep the logic for how to apply permissions to directories in one place and makes it easier to test.
|
|
451
|
+
class DirectorySpec
|
|
452
|
+
attr_reader :path, :owner, :group, :mode, :rights, :inherits
|
|
453
|
+
|
|
454
|
+
def initialize(path:, owner: nil, group: nil, mode: nil, rights: nil, inherits: :auto)
|
|
455
|
+
@path = path
|
|
456
|
+
@owner = owner
|
|
457
|
+
@group = group
|
|
458
|
+
@mode = mode
|
|
459
|
+
@rights = rights
|
|
460
|
+
@inherits = inherits
|
|
461
|
+
end
|
|
462
|
+
|
|
463
|
+
def windows?
|
|
464
|
+
Chef::Platform.windows?
|
|
465
|
+
end
|
|
466
|
+
|
|
467
|
+
def apply(resource)
|
|
468
|
+
resource.recursive true
|
|
469
|
+
|
|
470
|
+
if windows?
|
|
471
|
+
if rights && !rights.empty?
|
|
472
|
+
resource.inherits(inherits == :auto ? false : inherits)
|
|
473
|
+
|
|
474
|
+
rights.each do |principal, permission|
|
|
475
|
+
resource.rights(permission, principal)
|
|
476
|
+
end
|
|
477
|
+
else
|
|
478
|
+
# Ensure defaults can restore inherited ACL behavior after custom rights were applied.
|
|
479
|
+
resource.inherits(inherits == :auto ? true : inherits)
|
|
480
|
+
resource.mode(mode) if mode
|
|
481
|
+
end
|
|
482
|
+
|
|
483
|
+
resource.owner(owner) if owner
|
|
484
|
+
else
|
|
485
|
+
resource.owner(owner) if owner
|
|
486
|
+
resource.group(group) if group
|
|
487
|
+
resource.mode(mode) if mode
|
|
488
|
+
end
|
|
489
|
+
end
|
|
490
|
+
end
|
|
491
|
+
|
|
492
|
+
# A helper method to determine the directory to apply log permissions to based on the log_location property.
|
|
493
|
+
# If log_location is a file path, this returns the directory containing that file.
|
|
494
|
+
# If log_location is not a file path (e.g., it's a symbol for syslog or win_evt, or it's STDOUT/STDERR),
|
|
495
|
+
# this returns nil since there is no directory to apply permissions to.
|
|
496
|
+
def log_directory
|
|
497
|
+
return unless new_resource.log_location.is_a?(String)
|
|
498
|
+
return if %w{STDOUT STDERR}.include?(new_resource.log_location)
|
|
499
|
+
|
|
500
|
+
::File.dirname(new_resource.log_location)
|
|
501
|
+
end
|
|
336
502
|
end
|
|
337
503
|
end
|
|
338
504
|
end
|
|
@@ -65,7 +65,7 @@ class Chef
|
|
|
65
65
|
property :search, String, default: "*:*", desired_state: false,
|
|
66
66
|
description: "Search query that would match the same used for the clients, gets stored as a field in the item."
|
|
67
67
|
|
|
68
|
-
property :raw_data, [Hash, Mash], default: {},
|
|
68
|
+
property :raw_data, [Hash, Mash], default: {}, sensitive: true,
|
|
69
69
|
description: "The raw data, as a Ruby Hash, that will be stored in the item."
|
|
70
70
|
|
|
71
71
|
property :environment, [String, NilClass], desired_state: false,
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
require "cgi" unless defined?(CGI)
|
|
2
|
+
require "uri" unless defined?(URI)
|
|
3
|
+
|
|
1
4
|
class Chef
|
|
2
5
|
class Resource
|
|
3
6
|
class ChocolateyInstaller < Chef::Resource
|
|
@@ -70,7 +73,7 @@ class Chef
|
|
|
70
73
|
property :proxy_user, String,
|
|
71
74
|
description: "The username to use to build a proxy credential with. Will be consumed by the proxy_credential property if both this property and proxy_password are set"
|
|
72
75
|
|
|
73
|
-
property :proxy_password, String,
|
|
76
|
+
property :proxy_password, String, sensitive: true,
|
|
74
77
|
description: "The password to use to build a proxy credential with. Will be consumed by the proxy_credential property if both this property and proxy_user are set"
|
|
75
78
|
|
|
76
79
|
load_current_value do
|
|
@@ -91,6 +94,25 @@ class Chef
|
|
|
91
94
|
Gem::Version.new(get_choco_version)
|
|
92
95
|
end
|
|
93
96
|
|
|
97
|
+
def download_url_path
|
|
98
|
+
return download_url if download_url.match?(%r{^[a-zA-Z]:[\\/]}) || download_url.start_with?("\\\\")
|
|
99
|
+
|
|
100
|
+
::URI.parse(download_url).path
|
|
101
|
+
rescue URI::InvalidURIError
|
|
102
|
+
download_url
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def download_url_script?
|
|
106
|
+
::File.extname(download_url_path).casecmp(".ps1") == 0
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def download_destination
|
|
110
|
+
filename = ::File.basename(::CGI.unescape(download_url_path.to_s))
|
|
111
|
+
filename = "chocolatey.nupkg" if filename.empty? || ["/", "\\", "."].include?(filename)
|
|
112
|
+
|
|
113
|
+
Chef::Util::PathHelper.join(ChefConfig::Config.etc_chef_dir(windows: true), filename)
|
|
114
|
+
end
|
|
115
|
+
|
|
94
116
|
def define_resource_requirements
|
|
95
117
|
requirements.assert(:install, :upgrade).each do |a|
|
|
96
118
|
a.assertion do
|
|
@@ -133,16 +155,14 @@ class Chef
|
|
|
133
155
|
if new_resource.download_url
|
|
134
156
|
Chef::Log.info("Using custom download URL for Chocolatey installation: #{new_resource.download_url}")
|
|
135
157
|
# If it's a PowerShell script, execute it directly (original behavior)
|
|
136
|
-
if
|
|
158
|
+
if download_url_script?
|
|
137
159
|
powershell_exec("Invoke-Expression ((New-Object System.Net.WebClient).DownloadString('#{new_resource.download_url}'))").error!
|
|
138
160
|
else
|
|
139
161
|
# Assume it's a direct link to a nupkg or other file and handle accordingly
|
|
140
|
-
|
|
162
|
+
destination = download_destination
|
|
141
163
|
|
|
142
|
-
#
|
|
143
|
-
|
|
144
|
-
powershell_exec("Invoke-WebRequest '#{new_resource.download_url}' -OutFile '#{chef_dir}'").error!
|
|
145
|
-
Chef::Log.info("Downloaded file from #{new_resource.download_url} to #{chef_dir}")
|
|
164
|
+
powershell_exec("Invoke-WebRequest '#{new_resource.download_url}' -OutFile '#{destination}'").error!
|
|
165
|
+
Chef::Log.info("Downloaded file from #{new_resource.download_url} to #{destination}")
|
|
146
166
|
end
|
|
147
167
|
else
|
|
148
168
|
powershell_exec("Invoke-Expression ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))").error!
|
|
@@ -151,11 +171,9 @@ class Chef
|
|
|
151
171
|
end
|
|
152
172
|
|
|
153
173
|
action :upgrade, description: "Upgrades the Chocolatey package manager" do
|
|
154
|
-
if new_resource.chocolatey_version
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
proposed_version = nil
|
|
158
|
-
end
|
|
174
|
+
proposed_version = if new_resource.chocolatey_version
|
|
175
|
+
Gem::Version.new(new_resource.chocolatey_version)
|
|
176
|
+
end
|
|
159
177
|
|
|
160
178
|
if new_resource.download_url
|
|
161
179
|
powershell_exec("Set-Item -path env:ChocolateyDownloadUrl -Value #{new_resource.download_url}")
|
|
@@ -193,7 +211,7 @@ class Chef
|
|
|
193
211
|
end
|
|
194
212
|
|
|
195
213
|
action :uninstall, description: "Uninstall Chocolatey package manager" do
|
|
196
|
-
path =
|
|
214
|
+
path = 'c:\\programdata\\chocolatey\\bin'
|
|
197
215
|
if File.exists?(path)
|
|
198
216
|
converge_by("Uninstall Choco") do
|
|
199
217
|
powershell_code = <<~CODE
|
|
@@ -106,7 +106,7 @@ class Chef
|
|
|
106
106
|
property :channel, String, default: "stable",
|
|
107
107
|
description: "The release channel to install your package from."
|
|
108
108
|
|
|
109
|
-
property :auth_token, String,
|
|
109
|
+
property :auth_token, String, sensitive: true,
|
|
110
110
|
description: "Auth token for installing a package from a private organization on Habitat builder."
|
|
111
111
|
|
|
112
112
|
property :binlink, [true, false, :force], default: false,
|
|
@@ -129,10 +129,10 @@ class Chef
|
|
|
129
129
|
property :auto_update, [true, false], default: false,
|
|
130
130
|
description: "Passes `--auto-update`. This will set the Habitat supervisor to automatically update itself any time a stable version has been released."
|
|
131
131
|
|
|
132
|
-
property :auth_token, String,
|
|
132
|
+
property :auth_token, String, sensitive: true,
|
|
133
133
|
description: "Auth token for accessing a private organization on bldr. This value is templated into the appropriate service file."
|
|
134
134
|
|
|
135
|
-
property :gateway_auth_token, String,
|
|
135
|
+
property :gateway_auth_token, String, sensitive: true,
|
|
136
136
|
description: "Auth token for accessing the supervisor's HTTP gateway. This value is templated into the appropriate service file."
|
|
137
137
|
|
|
138
138
|
property :update_condition, String,
|
|
@@ -53,7 +53,7 @@ class Chef
|
|
|
53
53
|
property :remote_sup_http, String, default: "127.0.0.1:9631", desired_state: false,
|
|
54
54
|
description: "Address for remote supervisor http port. Used to pull existing."
|
|
55
55
|
|
|
56
|
-
property :gateway_auth_token, String, desired_state: false,
|
|
56
|
+
property :gateway_auth_token, String, sensitive: true, desired_state: false,
|
|
57
57
|
description: "Auth token for accessing the remote supervisor's http port."
|
|
58
58
|
|
|
59
59
|
property :user, String, desired_state: false,
|
|
@@ -118,7 +118,7 @@ class Chef
|
|
|
118
118
|
property :remote_sup_http, String, default: "127.0.0.1:9631", desired_state: false,
|
|
119
119
|
description: "IP address and port used to communicate with the remote supervisor. If this value is invalid, the resource will update the supervisor configuration each time #{ChefUtils::Dist::Server::PRODUCT} runs."
|
|
120
120
|
|
|
121
|
-
property :gateway_auth_token, String, desired_state: false,
|
|
121
|
+
property :gateway_auth_token, String, sensitive: true, desired_state: false,
|
|
122
122
|
description: "Auth token for accessing the remote supervisor's http port."
|
|
123
123
|
|
|
124
124
|
property :update_condition, [Symbol, String], equal_to: [:latest, "latest", :'track-channel', "track-channel"], default: :latest, coerce: proc { |s| s.is_a?(String) ? s.to_sym : s },
|
|
@@ -60,7 +60,7 @@ class Chef
|
|
|
60
60
|
description: "The desired curve of the generated key (if key_type is equal to 'ec'). Run openssl ecparam -list_curves to see available options.",
|
|
61
61
|
default: "prime256v1"
|
|
62
62
|
|
|
63
|
-
property :key_pass, String,
|
|
63
|
+
property :key_pass, String, sensitive: true,
|
|
64
64
|
description: "The desired passphrase for the key."
|
|
65
65
|
|
|
66
66
|
property :key_cipher, String,
|
|
@@ -56,10 +56,10 @@ class Chef
|
|
|
56
56
|
property :private_key_path, String,
|
|
57
57
|
description: "The path to the private key file."
|
|
58
58
|
|
|
59
|
-
property :private_key_content, String,
|
|
59
|
+
property :private_key_content, String, sensitive: true,
|
|
60
60
|
description: "The content of the private key including new lines. This property is used in place of private_key_path in instances where you want to avoid having to first write the private key to disk"
|
|
61
61
|
|
|
62
|
-
property :private_key_pass, String,
|
|
62
|
+
property :private_key_pass, String, sensitive: true,
|
|
63
63
|
description: "The passphrase of the provided private key."
|
|
64
64
|
|
|
65
65
|
property :owner, [String, Integer],
|
|
@@ -59,7 +59,7 @@ class Chef
|
|
|
59
59
|
description: "The desired bit length of the generated key.",
|
|
60
60
|
default: 2048
|
|
61
61
|
|
|
62
|
-
property :key_pass, String,
|
|
62
|
+
property :key_pass, String, sensitive: true,
|
|
63
63
|
description: "The desired passphrase for the key."
|
|
64
64
|
|
|
65
65
|
property :key_cipher, String,
|