chef 19.3.15 → 19.4.46
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 +5 -2
- data/chef.gemspec +5 -4
- data/lib/chef/application/base.rb +1 -5
- data/lib/chef/chef_fs/file_system/chef_server/organization_invites_entry.rb +1 -1
- data/lib/chef/cookbook_manifest.rb +18 -7
- data/lib/chef/cookbook_uploader.rb +1 -1
- data/lib/chef/cookbook_version.rb +3 -3
- data/lib/chef/data_bag.rb +3 -4
- data/lib/chef/data_collector.rb +1 -0
- data/lib/chef/encrypted_data_bag_item.rb +1 -1
- data/lib/chef/formatters/error_description.rb +8 -1
- data/lib/chef/http/auth_credentials.rb +1 -1
- data/lib/chef/http/authenticator.rb +18 -5
- data/lib/chef/http.rb +2 -2
- data/lib/chef/mixin/properties.rb +1 -1
- data/lib/chef/mixin/template.rb +24 -2
- data/lib/chef/property.rb +11 -1
- data/lib/chef/provider/group/dscl.rb +9 -5
- data/lib/chef/provider/group.rb +5 -0
- data/lib/chef/provider/mount/solaris.rb +10 -18
- data/lib/chef/provider/package/chocolatey.rb +2 -2
- data/lib/chef/provider/package/homebrew.rb +2 -3
- data/lib/chef/provider/package/portage.rb +0 -1
- data/lib/chef/provider/package/yum/python_helper.rb +0 -1
- data/lib/chef/provider/service/debian.rb +9 -9
- data/lib/chef/resource/apt_repository.rb +5 -1
- data/lib/chef/resource/archive_dll_locator.rb +70 -0
- data/lib/chef/resource/archive_file.rb +56 -27
- data/lib/chef/resource/chef_client_config.rb +187 -21
- data/lib/chef/resource/chef_client_hab_ca_cert.rb +10 -3
- data/lib/chef/resource/chef_vault_secret.rb +1 -1
- data/lib/chef/resource/chocolatey_installer.rb +41 -7
- data/lib/chef/resource/chocolatey_package.rb +1 -1
- data/lib/chef/resource/dnf_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/helpers/path_helpers.rb +2 -2
- data/lib/chef/resource/homebrew_cask.rb +11 -5
- data/lib/chef/resource/homebrew_tap.rb +18 -0
- data/lib/chef/resource/launchd.rb +1 -1
- data/lib/chef/resource/mount.rb +2 -2
- 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/service.rb +1 -1
- data/lib/chef/resource/sysctl.rb +22 -5
- data/lib/chef/resource/windows_ad_join.rb +1 -1
- data/lib/chef/resource/windows_certificate.rb +1 -1
- data/lib/chef/resource/windows_security_policy.rb +0 -1
- data/lib/chef/resource/windows_service.rb +1 -1
- data/lib/chef/resource/windows_task.rb +1 -3
- data/lib/chef/resource/windows_update_settings.rb +51 -30
- data/lib/chef/resource/yum_package.rb +1 -1
- data/lib/chef/resource/yum_repository.rb +2 -2
- data/lib/chef/resource.rb +2 -2
- data/lib/chef/resource_inspector.rb +1 -1
- data/lib/chef/role.rb +1 -1
- data/lib/chef/target_io/train/dir.rb +10 -1
- data/lib/chef/target_io/train/fileutils.rb +1 -1
- data/lib/chef/util/selinux.rb +2 -2
- data/lib/chef/version.rb +1 -1
- data/lib/chef/win32/api/command_line_helper.rb +2 -2
- data/lib/chef/win32/api/error.rb +0 -2
- data/lib/chef/win32/api/net.rb +2 -4
- data/lib/chef/win32/api.rb +4 -4
- data/lib/chef/win32/net.rb +0 -1
- data/lib/chef/win32/security/acl.rb +1 -1
- data/lib/chef/win32/security.rb +1 -1
- metadata +18 -11
|
@@ -0,0 +1,70 @@
|
|
|
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 "open3" unless defined?(Open3)
|
|
19
|
+
|
|
20
|
+
class Chef
|
|
21
|
+
class Resource
|
|
22
|
+
# Locates archive.dll for a Habitat-packaged Chef on Windows. Sources are
|
|
23
|
+
# tried from most to least authoritative so that a stray or older libarchive
|
|
24
|
+
# package on the machine is only used when nothing more specific exists:
|
|
25
|
+
# 1. RUBY_DLL_PATH (set by the Habitat plan)
|
|
26
|
+
# 2. the hab CLI (`hab pkg path core/libarchive`)
|
|
27
|
+
# 3. a search of the hab root derived from the location of chef itself
|
|
28
|
+
module ArchiveDllLocator
|
|
29
|
+
DLL_NAME = "archive.dll".freeze
|
|
30
|
+
|
|
31
|
+
def self.find(source_file: __FILE__, env: ::ENV)
|
|
32
|
+
from_ruby_dll_path(env) || from_hab_cli || from_hab_root_search(source_file)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def self.from_ruby_dll_path(env = ::ENV)
|
|
36
|
+
env["RUBY_DLL_PATH"].to_s.split(::File::PATH_SEPARATOR).each do |dir|
|
|
37
|
+
next if dir.empty?
|
|
38
|
+
|
|
39
|
+
candidate = ::File.join(dir.tr("\\", "/"), DLL_NAME)
|
|
40
|
+
return candidate if ::File.exist?(candidate)
|
|
41
|
+
end
|
|
42
|
+
nil
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def self.from_hab_cli
|
|
46
|
+
stdout, stderr, status = Open3.capture3("hab pkg path core/libarchive")
|
|
47
|
+
unless status.success?
|
|
48
|
+
Chef::Log.debug("Failed to determine Habitat libarchive path: #{stderr}")
|
|
49
|
+
return nil
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
candidate = ::File.join(stdout.strip.tr("\\", "/"), "bin", DLL_NAME)
|
|
53
|
+
return candidate if ::File.exist?(candidate)
|
|
54
|
+
|
|
55
|
+
Chef::Log.debug("#{DLL_NAME} not found in Habitat path: #{candidate}")
|
|
56
|
+
nil
|
|
57
|
+
rescue SystemCallError => e
|
|
58
|
+
Chef::Log.debug("hab CLI not available to locate libarchive: #{e.message}")
|
|
59
|
+
nil
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def self.from_hab_root_search(source_file)
|
|
63
|
+
hab_root = source_file.tr("\\", "/")[%r{\A(.*?/hab)/pkgs/}, 1]
|
|
64
|
+
return nil unless hab_root
|
|
65
|
+
|
|
66
|
+
::Dir.glob("#{hab_root}/pkgs/core/libarchive/*/*/bin/#{DLL_NAME}").max
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
|
|
21
21
|
require_relative "../resource"
|
|
22
22
|
require "fileutils" unless defined?(FileUtils)
|
|
23
|
+
|
|
23
24
|
begin
|
|
24
25
|
# The explicit call to FFI::DynamicLibrary.open was added to address an issue specific to the Habitat packaging of Chef Infra Client on windows.
|
|
25
26
|
# In the Habitat environment, the libarchive library (archive.dll) is installed in a non-standard location that is not included
|
|
@@ -37,34 +38,29 @@ begin
|
|
|
37
38
|
# This code dynamically determines the path to archive.dll using the Habitat CLI (`hab pkg path core/libarchive`) and explicitly
|
|
38
39
|
# loads the library using FFI::DynamicLibrary.open. This ensures that the library is correctly loaded in the Habitat environment.
|
|
39
40
|
#
|
|
40
|
-
# Note: This logic is gated by a check
|
|
41
|
-
#
|
|
42
|
-
#
|
|
43
|
-
#
|
|
44
|
-
|
|
41
|
+
# Note: This logic is gated by a check that we are running from a Habitat package
|
|
42
|
+
# path to ensure it is only applied in Habitat runs. For other environments
|
|
43
|
+
# (e.g., Omnibus, plain gem installations, or git checkouts), the default behavior
|
|
44
|
+
# of FFI is sufficient, as the libraries are installed in standard locations or
|
|
45
|
+
# embedded paths that are included in the default search paths.
|
|
46
|
+
# Detect Habitat runtime environment reliably. HAB_CACHE_SRC_PATH is only set
|
|
47
|
+
# during `hab pkg build` (studio), not at runtime. HAB_PKG_PATH is also not
|
|
48
|
+
# guaranteed at runtime. Checking __FILE__ is reliable: when chef-client runs
|
|
49
|
+
# from a Hab package via `hab pkg exec`, this file's path always contains
|
|
50
|
+
# "hab/pkgs".
|
|
51
|
+
if RUBY_PLATFORM.match?(/mswin|mingw|windows/) && __FILE__.match?(%r{hab[/\\]pkgs})
|
|
45
52
|
require "ffi" unless defined?(FFI)
|
|
46
|
-
|
|
47
|
-
# Dynamically determine the path to the core/libarchive package
|
|
48
|
-
stdout, stderr, status = Open3.capture3("hab pkg path core/libarchive")
|
|
49
|
-
unless status.success?
|
|
50
|
-
Chef::Log.debug("Failed to determine Habitat libarchive path: #{stderr}")
|
|
51
|
-
return
|
|
52
|
-
end
|
|
53
|
+
require_relative "archive_dll_locator"
|
|
53
54
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
Chef::Log.debug("Habitat libarchive path not found: #{habitat_libarchive_path}")
|
|
57
|
-
return
|
|
58
|
-
end
|
|
55
|
+
# Search order: RUBY_DLL_PATH, then the hab CLI, then a search of the hab root.
|
|
56
|
+
archive_dll_path = Chef::Resource::ArchiveDllLocator.find(source_file: __FILE__)
|
|
59
57
|
|
|
60
|
-
archive_dll_path
|
|
61
|
-
|
|
62
|
-
Chef::Log.debug("archive.dll
|
|
63
|
-
|
|
58
|
+
if archive_dll_path
|
|
59
|
+
FFI::DynamicLibrary.open(archive_dll_path, FFI::DynamicLibrary::RTLD_LAZY)
|
|
60
|
+
Chef::Log.debug("Explicitly loaded archive.dll from Habitat path: #{archive_dll_path}")
|
|
61
|
+
else
|
|
62
|
+
Chef::Log.debug("archive.dll not located via Habitat; relying on default library search paths")
|
|
64
63
|
end
|
|
65
|
-
|
|
66
|
-
FFI::DynamicLibrary.open(archive_dll_path, FFI::DynamicLibrary::RTLD_LAZY) # Explicitly load the DLL
|
|
67
|
-
Chef::Log.debug("Explicitly loaded archive.dll from Habitat path: #{archive_dll_path}")
|
|
68
64
|
end
|
|
69
65
|
|
|
70
66
|
# ffi-libarchive must be eager loaded see: https://github.com/chef/chef/issues/12228
|
|
@@ -171,8 +167,14 @@ class Chef
|
|
|
171
167
|
|
|
172
168
|
if new_resource.owner || new_resource.group
|
|
173
169
|
converge_by("set owner of files extracted in #{new_resource.destination} to #{new_resource.owner}:#{new_resource.group}") do
|
|
170
|
+
dest_realpath = ::File.expand_path(new_resource.destination)
|
|
174
171
|
Archive::Reader.open_filename(new_resource.path, nil, strip_components: new_resource.strip_components) do |archive|
|
|
175
172
|
archive.each_entry do |e|
|
|
173
|
+
# Validate path is within destination before chown-ing.
|
|
174
|
+
# Use expand_path for security check, original string for the chown call.
|
|
175
|
+
chown_path_expanded = ::File.expand_path("#{new_resource.destination}/#{e.pathname}")
|
|
176
|
+
next unless chown_path_expanded.start_with?(dest_realpath + ::File::SEPARATOR) || chown_path_expanded == dest_realpath
|
|
177
|
+
|
|
176
178
|
FileUtils.chown(new_resource.owner, new_resource.group, "#{new_resource.destination}/#{e.pathname}")
|
|
177
179
|
end
|
|
178
180
|
end
|
|
@@ -211,10 +213,16 @@ class Chef
|
|
|
211
213
|
# @return [Boolean]
|
|
212
214
|
def archive_differs_from_disk?(src, dest)
|
|
213
215
|
modified = false
|
|
216
|
+
dest_realpath = ::File.expand_path(dest)
|
|
214
217
|
Archive::Reader.open_filename(src, nil, strip_components: new_resource.strip_components) do |archive|
|
|
215
218
|
Chef::Log.trace("Beginning the comparison of file mtime between contents of #{src} and #{dest}")
|
|
216
219
|
archive.each_entry do |e|
|
|
217
|
-
pathname = ::File.expand_path(e.pathname,
|
|
220
|
+
pathname = ::File.expand_path(e.pathname, dest_realpath)
|
|
221
|
+
# Skip archive entries that resolve outside the destination directory.
|
|
222
|
+
# A traversal entry (e.g. "../escaped") must not influence the mtime
|
|
223
|
+
# comparison and must never trigger extraction.
|
|
224
|
+
next unless pathname.start_with?(dest_realpath + ::File::SEPARATOR) || pathname == dest_realpath
|
|
225
|
+
|
|
218
226
|
if ::File.exist?(pathname)
|
|
219
227
|
Chef::Log.trace("#{pathname} mtime is #{::File.mtime(pathname)} and archive is #{e.mtime}")
|
|
220
228
|
modified = true unless ::File.mtime(pathname) == e.mtime
|
|
@@ -236,12 +244,33 @@ class Chef
|
|
|
236
244
|
# @return [void]
|
|
237
245
|
def extract(src, dest, options = [])
|
|
238
246
|
converge_by("extract #{src} to #{dest}") do
|
|
239
|
-
|
|
247
|
+
user_flags = [options].flatten.map { |option| extract_option_map[option] }.compact.reduce(:|).to_i
|
|
248
|
+
# Always apply libarchive security flags to prevent path traversal.
|
|
249
|
+
# These are not user-overridable hence directly added here
|
|
250
|
+
# instead of options array. NODOTDOT blocks "../" sequences,
|
|
251
|
+
# NOABSOLUTEPATHS blocks absolute-path entries, SECURE_SYMLINKS prevents
|
|
252
|
+
# symlink attacks. Combined with the check below in archive.each_entry
|
|
253
|
+
# they safeguard against Zip Slip / CWE-22 attacks.
|
|
254
|
+
security_flags = Archive::EXTRACT_SECURE_NODOTDOT |
|
|
255
|
+
Archive::EXTRACT_SECURE_NOABSOLUTEPATHS |
|
|
256
|
+
Archive::EXTRACT_SECURE_SYMLINKS
|
|
257
|
+
all_flags = user_flags | security_flags
|
|
258
|
+
dest_realpath = ::File.expand_path(dest)
|
|
240
259
|
|
|
241
260
|
Dir.chdir(dest) do
|
|
242
261
|
Archive::Reader.open_filename(src, nil, strip_components: new_resource.strip_components) do |archive|
|
|
243
262
|
archive.each_entry do |e|
|
|
244
|
-
|
|
263
|
+
# reject any entry whose resolved path
|
|
264
|
+
# falls outside the destination directory before calling into libarchive.
|
|
265
|
+
entry_realpath = ::File.expand_path(e.pathname, dest_realpath)
|
|
266
|
+
unless entry_realpath.start_with?(dest_realpath + ::File::SEPARATOR) || entry_realpath == dest_realpath
|
|
267
|
+
raise Chef::Exceptions::ValidationFailed,
|
|
268
|
+
"Refusing to extract archive entry #{e.pathname.inspect}: resolved path " \
|
|
269
|
+
"#{entry_realpath.inspect} is outside destination #{dest_realpath.inspect}. " \
|
|
270
|
+
"This archive may contain a path traversal attack."
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
archive.extract(e, all_flags)
|
|
245
274
|
end
|
|
246
275
|
end
|
|
247
276
|
end
|
|
@@ -21,6 +21,14 @@ class Chef
|
|
|
21
21
|
class Resource
|
|
22
22
|
class ChefClientConfig < Chef::Resource
|
|
23
23
|
|
|
24
|
+
DIRECTORY_SPEC_DEFAULTS = {
|
|
25
|
+
config: { mode: "0750", inherits: :auto },
|
|
26
|
+
client_d: { mode: "0750", inherits: :auto },
|
|
27
|
+
logs: { mode: "0755", inherits: :auto },
|
|
28
|
+
cache: { mode: "0750", inherits: :auto },
|
|
29
|
+
backups: { mode: "0750", inherits: :auto },
|
|
30
|
+
}.freeze
|
|
31
|
+
|
|
24
32
|
provides :chef_client_config, target_mode: true
|
|
25
33
|
target_mode support: :full
|
|
26
34
|
|
|
@@ -77,12 +85,12 @@ class Chef
|
|
|
77
85
|
chef_server_url 'https://chef.example.dmz'
|
|
78
86
|
report_handlers [
|
|
79
87
|
{
|
|
80
|
-
|
|
81
|
-
|
|
88
|
+
'class' => 'ReportHandler1Class',
|
|
89
|
+
'arguments' => ["'FirstArgument'", "'SecondArgument'"],
|
|
82
90
|
},
|
|
83
91
|
{
|
|
84
|
-
|
|
85
|
-
|
|
92
|
+
'class' => 'ReportHandler2Class',
|
|
93
|
+
'arguments' => ["'FirstArgument'", "'SecondArgument'"],
|
|
86
94
|
},
|
|
87
95
|
]
|
|
88
96
|
end
|
|
@@ -97,6 +105,48 @@ class Chef
|
|
|
97
105
|
data_collector_token 'TEST_TOKEN_TEST'
|
|
98
106
|
end
|
|
99
107
|
```
|
|
108
|
+
|
|
109
|
+
**Set Linux directory ownership and modes**:
|
|
110
|
+
|
|
111
|
+
```ruby
|
|
112
|
+
chef_client_config 'Create client.rb' do
|
|
113
|
+
chef_server_url 'https://chef.example.dmz'
|
|
114
|
+
user 'root'
|
|
115
|
+
group 'root'
|
|
116
|
+
file_cache_path '/var/chef/cache'
|
|
117
|
+
file_backup_path '/var/chef/backups'
|
|
118
|
+
log_location '/var/log/chef/client.log'
|
|
119
|
+
directory_specs(
|
|
120
|
+
config: { owner: 'root', group: 'root', mode: '0700' },
|
|
121
|
+
client_d: { owner: 'root', group: 'root', mode: '0700' },
|
|
122
|
+
logs: { owner: 'root', group: 'root', mode: '0750' },
|
|
123
|
+
cache: { owner: 'root', group: 'root', mode: '0750' },
|
|
124
|
+
backups: { owner: 'root', group: 'root', mode: '0750' }
|
|
125
|
+
)
|
|
126
|
+
end
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
**Set Windows directory inheritance and rights**:
|
|
130
|
+
|
|
131
|
+
```ruby
|
|
132
|
+
chef_client_config 'Create client.rb' do
|
|
133
|
+
chef_server_url 'https://chef.example.dmz'
|
|
134
|
+
config_directory 'C:/chef'
|
|
135
|
+
file_cache_path 'C:/chef/cache'
|
|
136
|
+
file_backup_path 'C:/chef/backups'
|
|
137
|
+
log_location 'C:/chef/log/client.log'
|
|
138
|
+
directory_specs(
|
|
139
|
+
config: {
|
|
140
|
+
owner: 'Administrators',
|
|
141
|
+
inherits: false,
|
|
142
|
+
rights: {
|
|
143
|
+
'Administrators' => :full_control,
|
|
144
|
+
'SYSTEM' => :full_control,
|
|
145
|
+
},
|
|
146
|
+
}
|
|
147
|
+
)
|
|
148
|
+
end
|
|
149
|
+
```
|
|
100
150
|
DOC
|
|
101
151
|
|
|
102
152
|
# @todo policy_file or policy_group being set requires the other to be set so enforce that.
|
|
@@ -120,7 +170,7 @@ class Chef
|
|
|
120
170
|
property :config_directory, String,
|
|
121
171
|
description: "The directory to store the client.rb in.",
|
|
122
172
|
default: ChefConfig::Config.etc_chef_dir,
|
|
123
|
-
default_description:
|
|
173
|
+
default_description: '`/etc/chef/` on *nix-like systems and `C:\\chef\\` on Windows'
|
|
124
174
|
|
|
125
175
|
property :user, String,
|
|
126
176
|
description: "The user that should own the client.rb file and the configuration directory if it needs to be created. Note: The configuration directory will not be created if it already exists, which allows you to further control the setup of that directory outside of this resource."
|
|
@@ -212,7 +262,7 @@ class Chef
|
|
|
212
262
|
default: []
|
|
213
263
|
|
|
214
264
|
property :rubygems_url, [String, Array],
|
|
215
|
-
description:
|
|
265
|
+
description: 'The location to source rubygems. It can be set to a string or array of strings for URIs to set as rubygems sources. This allows individuals to setup an internal mirror of rubygems for "airgapped" environments.',
|
|
216
266
|
introduced: "17.11"
|
|
217
267
|
|
|
218
268
|
property :exception_handlers, Array,
|
|
@@ -255,21 +305,83 @@ class Chef
|
|
|
255
305
|
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.",
|
|
256
306
|
introduced: "17.8"
|
|
257
307
|
|
|
308
|
+
property :directory_specs, Hash,
|
|
309
|
+
description: <<~DESC,
|
|
310
|
+
Permission overrides for Chef-managed directories.
|
|
311
|
+
This must be a hash of hashes: top-level keys are directory names, values are per-directory option hashes.
|
|
312
|
+
|
|
313
|
+
Use this property like:
|
|
314
|
+
directory_specs(
|
|
315
|
+
config: { owner: "root", group: "root", mode: "0700" },
|
|
316
|
+
client_d: { owner: "root", group: "root", mode: "0700" },
|
|
317
|
+
logs: { owner: "root", group: "root", mode: "0700" },
|
|
318
|
+
cache: { owner: "root", group: "root", mode: "0700" },
|
|
319
|
+
backups: { owner: "root", group: "root", mode: "0700" }
|
|
320
|
+
)
|
|
321
|
+
|
|
322
|
+
Directory keys: :config, :client_d, :logs, :cache, :backups
|
|
323
|
+
Override keys: :owner, :group, :mode, :rights, :inherits (:rights and :inherits are Windows-only)
|
|
324
|
+
DESC
|
|
325
|
+
callbacks: {
|
|
326
|
+
"keys must be one of :config, :client_d, :logs, :cache, :backups" => lambda { |v|
|
|
327
|
+
valid_keys = %i{config client_d logs cache backups}
|
|
328
|
+
v.keys.all? { |k| valid_keys.include?(k) }
|
|
329
|
+
},
|
|
330
|
+
"values must be a hash with keys only from :owner, :group, :mode, :rights, :inherits" => lambda { |v|
|
|
331
|
+
valid_inner_keys = %i{owner group mode rights inherits}
|
|
332
|
+
v.values.all? { |value| value.is_a?(Hash) && value.keys.all? { |k| valid_inner_keys.include?(k) } }
|
|
333
|
+
},
|
|
334
|
+
},
|
|
335
|
+
default: DIRECTORY_SPEC_DEFAULTS,
|
|
336
|
+
coerce: proc { |v|
|
|
337
|
+
DIRECTORY_SPEC_DEFAULTS.merge(v) do |_key, base, override|
|
|
338
|
+
override.is_a?(Hash) ? base.merge(override) : override
|
|
339
|
+
end
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
property :client_rb_mode, String,
|
|
343
|
+
description: "The mode to set on the client.rb file that is written out by this resource (e.g., 0600).",
|
|
344
|
+
default: "0640"
|
|
345
|
+
|
|
258
346
|
action :create, description: "Create a client.rb config file and folders for configuring #{ChefUtils::Dist::Infra::PRODUCT}." do
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
347
|
+
# The logs directory is derived from log_location, which may be a symbol
|
|
348
|
+
# or a stream name (STDOUT/STDERR) rather than a file path. Only raise if
|
|
349
|
+
# the user customized logs specs but log_location is not a usable file path.
|
|
350
|
+
if new_resource.directory_specs[:logs] != DIRECTORY_SPEC_DEFAULTS[:logs] && log_directory.nil?
|
|
351
|
+
raise ArgumentError, "Invalid directory_specs: logs requires :logs (file path)"
|
|
352
|
+
end
|
|
353
|
+
|
|
354
|
+
# Build path map for each managed directory. For cache and backups, fall
|
|
355
|
+
# back to ChefConfig defaults when not explicitly set so that directory_specs
|
|
356
|
+
# overrides are applied even if the user didn't configure a custom path in client.rb.
|
|
357
|
+
spec_paths = {
|
|
358
|
+
config: new_resource.config_directory,
|
|
359
|
+
client_d: ::File.join(new_resource.config_directory, "client.d"),
|
|
360
|
+
logs: log_directory,
|
|
361
|
+
cache: new_resource.file_cache_path || ChefConfig::Config.file_cache_path,
|
|
362
|
+
backups: new_resource.file_backup_path || ChefConfig::Config.file_backup_path,
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
# Build directory specs based on user input and defaults
|
|
366
|
+
specs = {}
|
|
367
|
+
new_resource.directory_specs.each do |key, overrides|
|
|
368
|
+
path = spec_paths[key]
|
|
369
|
+
next if path.nil?
|
|
370
|
+
|
|
371
|
+
specs[key] = DirectorySpec.new(
|
|
372
|
+
path: path,
|
|
373
|
+
owner: overrides[:owner] || new_resource.user,
|
|
374
|
+
group: overrides[:group] || new_resource.group,
|
|
375
|
+
mode: overrides[:mode],
|
|
376
|
+
rights: overrides[:rights],
|
|
377
|
+
inherits: overrides[:inherits]
|
|
378
|
+
)
|
|
379
|
+
end
|
|
380
|
+
|
|
381
|
+
# Converge directories
|
|
382
|
+
specs.each_value do |spec|
|
|
383
|
+
directory spec.path do
|
|
384
|
+
spec.apply(self)
|
|
273
385
|
end
|
|
274
386
|
end
|
|
275
387
|
|
|
@@ -310,7 +422,7 @@ class Chef
|
|
|
310
422
|
data_collector_server_url: new_resource.data_collector_server_url,
|
|
311
423
|
data_collector_token: new_resource.data_collector_token
|
|
312
424
|
)
|
|
313
|
-
mode
|
|
425
|
+
mode new_resource.client_rb_mode
|
|
314
426
|
action :create
|
|
315
427
|
end
|
|
316
428
|
end
|
|
@@ -334,6 +446,60 @@ class Chef
|
|
|
334
446
|
"#{handler["class"]}.new(#{handler["arguments"].join(",")})"
|
|
335
447
|
end
|
|
336
448
|
end
|
|
449
|
+
|
|
450
|
+
# A helper class to encapsulate directory spec data and application logic for both *nix and Windows.
|
|
451
|
+
# This allows us to keep the logic for how to apply permissions to directories in one place and makes it easier to test.
|
|
452
|
+
class DirectorySpec
|
|
453
|
+
attr_reader :path, :owner, :group, :mode, :rights, :inherits
|
|
454
|
+
|
|
455
|
+
def initialize(path:, owner: nil, group: nil, mode: nil, rights: nil, inherits: :auto)
|
|
456
|
+
@path = path
|
|
457
|
+
@owner = owner
|
|
458
|
+
@group = group
|
|
459
|
+
@mode = mode
|
|
460
|
+
@rights = rights
|
|
461
|
+
@inherits = inherits
|
|
462
|
+
end
|
|
463
|
+
|
|
464
|
+
def windows?
|
|
465
|
+
Chef::Platform.windows?
|
|
466
|
+
end
|
|
467
|
+
|
|
468
|
+
def apply(resource)
|
|
469
|
+
resource.recursive true
|
|
470
|
+
|
|
471
|
+
if windows?
|
|
472
|
+
if rights && !rights.empty?
|
|
473
|
+
resource.inherits(inherits == :auto ? false : inherits)
|
|
474
|
+
|
|
475
|
+
rights.each do |principal, permission|
|
|
476
|
+
resource.rights(permission, principal)
|
|
477
|
+
end
|
|
478
|
+
else
|
|
479
|
+
# Ensure defaults can restore inherited ACL behavior after custom rights were applied.
|
|
480
|
+
resource.inherits(inherits == :auto ? true : inherits)
|
|
481
|
+
resource.mode(mode) if mode
|
|
482
|
+
end
|
|
483
|
+
|
|
484
|
+
resource.owner(owner) if owner
|
|
485
|
+
else
|
|
486
|
+
resource.owner(owner) if owner
|
|
487
|
+
resource.group(group) if group
|
|
488
|
+
resource.mode(mode) if mode
|
|
489
|
+
end
|
|
490
|
+
end
|
|
491
|
+
end
|
|
492
|
+
|
|
493
|
+
# A helper method to determine the directory to apply log permissions to based on the log_location property.
|
|
494
|
+
# If log_location is a file path, this returns the directory containing that file.
|
|
495
|
+
# If log_location is not a file path (e.g., it's a symbol for syslog or win_evt, or it's STDOUT/STDERR),
|
|
496
|
+
# this returns nil since there is no directory to apply permissions to.
|
|
497
|
+
def log_directory
|
|
498
|
+
return unless new_resource.log_location.is_a?(String)
|
|
499
|
+
return if %w{STDOUT STDERR}.include?(new_resource.log_location)
|
|
500
|
+
|
|
501
|
+
::File.dirname(new_resource.log_location)
|
|
502
|
+
end
|
|
337
503
|
end
|
|
338
504
|
end
|
|
339
505
|
end
|
|
@@ -104,10 +104,17 @@ class Chef
|
|
|
104
104
|
|
|
105
105
|
# Extract package ident from path: /hab/pkgs/chef/chef-infra-client/VERSION/RELEASE/bin/chef-client
|
|
106
106
|
# or: C:\hab\pkgs\chef\chef-infra-client\VERSION\RELEASE\bin\chef-client.exe
|
|
107
|
+
# or: /opt/hab/pkgs/chef/chef-infra-client/VERSION/RELEASE/bin/chef-client (hab root not "/hab")
|
|
107
108
|
# Result should be: chef/chef-infra-client/VERSION/RELEASE
|
|
108
|
-
|
|
109
|
+
filename_parts = Pathname.new(current_chef_path).each_filename.to_a
|
|
110
|
+
pkgs_index = filename_parts.index("pkgs")
|
|
111
|
+
if pkgs_index.nil?
|
|
112
|
+
raise "Unable to determine habitat package ident from #{ChefUtils::Dist::Infra::CLIENT} path '#{current_chef_path}'."
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
package_ident = ::File.join(filename_parts[(pkgs_index + 1)..(pkgs_index + 4)])
|
|
109
116
|
|
|
110
|
-
ca_pkg = shell_out("#{current_hab_path} pkg dependencies #{package_ident}")
|
|
117
|
+
ca_pkg = shell_out("#{current_hab_path} pkg dependencies #{package_ident}", environment: { "HAB_LICENSE" => "accept-no-persist" })
|
|
111
118
|
if ca_pkg.error?
|
|
112
119
|
raise "Failed to determine CA Certs for the #{ChefUtils::Dist::Infra::PRODUCT}'s habitat package"
|
|
113
120
|
end
|
|
@@ -118,7 +125,7 @@ class Chef
|
|
|
118
125
|
raise "Unable to find 'core/cacerts' package in dependencies. Failed to determine CA Certs."
|
|
119
126
|
end
|
|
120
127
|
|
|
121
|
-
ca_path = shell_out("#{current_hab_path} pkg path #{hab_cacerts_pkg}")
|
|
128
|
+
ca_path = shell_out("#{current_hab_path} pkg path #{hab_cacerts_pkg}", environment: { "HAB_LICENSE" => "accept-no-persist" })
|
|
122
129
|
if ca_path.error?
|
|
123
130
|
raise "Unable to find path for the 'core/cacerts' habitat package."
|
|
124
131
|
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
|
|
@@ -131,16 +153,28 @@ class Chef
|
|
|
131
153
|
# note that Invoke-Expression is being called on the downloaded script (outer parens),
|
|
132
154
|
# not triggering the script download (inner parens)
|
|
133
155
|
converge_if_changed do
|
|
134
|
-
|
|
156
|
+
if new_resource.download_url
|
|
157
|
+
Chef::Log.info("Using custom download URL for Chocolatey installation: #{new_resource.download_url}")
|
|
158
|
+
# If it's a PowerShell script, execute it directly (original behavior)
|
|
159
|
+
if download_url_script?
|
|
160
|
+
powershell_exec("Invoke-Expression ((New-Object System.Net.WebClient).DownloadString('#{new_resource.download_url}'))").error!
|
|
161
|
+
else
|
|
162
|
+
# Assume it's a direct link to a nupkg or other file and handle accordingly
|
|
163
|
+
destination = download_destination
|
|
164
|
+
|
|
165
|
+
powershell_exec("Invoke-WebRequest '#{new_resource.download_url}' -OutFile '#{destination}'").error!
|
|
166
|
+
Chef::Log.info("Downloaded file from #{new_resource.download_url} to #{destination}")
|
|
167
|
+
end
|
|
168
|
+
else
|
|
169
|
+
powershell_exec("Invoke-Expression ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))").error!
|
|
170
|
+
end
|
|
135
171
|
end
|
|
136
172
|
end
|
|
137
173
|
|
|
138
174
|
action :upgrade, description: "Upgrades the Chocolatey package manager" do
|
|
139
|
-
if new_resource.chocolatey_version
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
proposed_version = nil
|
|
143
|
-
end
|
|
175
|
+
proposed_version = if new_resource.chocolatey_version
|
|
176
|
+
Gem::Version.new(new_resource.chocolatey_version)
|
|
177
|
+
end
|
|
144
178
|
|
|
145
179
|
if new_resource.download_url
|
|
146
180
|
powershell_exec("Set-Item -path env:ChocolateyDownloadUrl -Value #{new_resource.download_url}")
|
|
@@ -108,7 +108,7 @@ class Chef
|
|
|
108
108
|
property :channel, String, default: "stable",
|
|
109
109
|
description: "The release channel to install your package from."
|
|
110
110
|
|
|
111
|
-
property :auth_token, String,
|
|
111
|
+
property :auth_token, String, sensitive: true,
|
|
112
112
|
description: "Auth token for installing a package from a private organization on Habitat builder."
|
|
113
113
|
|
|
114
114
|
property :binlink, [true, false, :force], default: false,
|
|
@@ -130,10 +130,10 @@ class Chef
|
|
|
130
130
|
property :auto_update, [true, false], default: false,
|
|
131
131
|
description: "Passes `--auto-update`. This will set the Habitat supervisor to automatically update itself any time a stable version has been released."
|
|
132
132
|
|
|
133
|
-
property :auth_token, String,
|
|
133
|
+
property :auth_token, String, sensitive: true,
|
|
134
134
|
description: "Auth token for accessing a private organization on bldr. This value is templated into the appropriate service file."
|
|
135
135
|
|
|
136
|
-
property :gateway_auth_token, String,
|
|
136
|
+
property :gateway_auth_token, String, sensitive: true,
|
|
137
137
|
description: "Auth token for accessing the supervisor's HTTP gateway. This value is templated into the appropriate service file."
|
|
138
138
|
|
|
139
139
|
property :update_condition, String,
|
|
@@ -56,7 +56,7 @@ class Chef
|
|
|
56
56
|
property :remote_sup_http, String, default: "127.0.0.1:9631", desired_state: false,
|
|
57
57
|
description: "Address for remote supervisor http port. Used to pull existing."
|
|
58
58
|
|
|
59
|
-
property :gateway_auth_token, String, desired_state: false,
|
|
59
|
+
property :gateway_auth_token, String, sensitive: true, desired_state: false,
|
|
60
60
|
description: "Auth token for accessing the remote supervisor's http port."
|
|
61
61
|
|
|
62
62
|
property :user, String, desired_state: false,
|
|
@@ -119,7 +119,7 @@ class Chef
|
|
|
119
119
|
property :remote_sup_http, String, default: "127.0.0.1:9631", desired_state: false,
|
|
120
120
|
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."
|
|
121
121
|
|
|
122
|
-
property :gateway_auth_token, String, desired_state: false,
|
|
122
|
+
property :gateway_auth_token, String, sensitive: true, desired_state: false,
|
|
123
123
|
description: "Auth token for accessing the remote supervisor's http port."
|
|
124
124
|
|
|
125
125
|
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 },
|