1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
|
#
# Author:: Daniel DeLeo (<dan@chef.io>)
# Copyright:: Copyright 2011-2016, Chef Software, Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
require "chef/run_list"
require "chef/util/path_helper"
require "pathname"
require "chef/dist"
class Chef
class Knife
module Core
# Instances of BootstrapContext are the context objects (i.e., +self+) for
# bootstrap templates. For backwards compatibility, they +must+ set the
# following instance variables:
# * @config - a hash of knife's config values
# * @run_list - the run list for the node to boostrap
#
class BootstrapContext
attr_accessor :client_pem
def initialize(config, run_list, chef_config, secret = nil)
@config = config
@run_list = run_list
@chef_config = chef_config
@secret = secret
end
def bootstrap_environment
@config[:environment]
end
def validation_key
if @chef_config[:validation_key] &&
File.exist?(File.expand_path(@chef_config[:validation_key]))
IO.read(File.expand_path(@chef_config[:validation_key]))
else
false
end
end
def client_d
@client_d ||= client_d_content
end
def encrypted_data_bag_secret
@secret
end
# Contains commands and content, see trusted_certs_content
# @todo Rename to trusted_certs_script
def trusted_certs
@trusted_certs ||= trusted_certs_content
end
def get_log_location
if !(@chef_config[:config_log_location].class == IO ) && (@chef_config[:config_log_location].nil? || @chef_config[:config_log_location].to_s.empty?)
"STDOUT"
elsif @chef_config[:config_log_location].equal?(:win_evt)
raise "The value :win_evt is not supported for config_log_location on Linux Platforms \n"
elsif @chef_config[:config_log_location].equal?(:syslog)
":syslog"
elsif @chef_config[:config_log_location].equal?(STDOUT)
"STDOUT"
elsif @chef_config[:config_log_location].equal?(STDERR)
"STDERR"
elsif @chef_config[:config_log_location]
%Q{"#{@chef_config[:config_log_location]}"}
else
"STDOUT"
end
end
def config_content
client_rb = <<~CONFIG
chef_server_url "#{@chef_config[:chef_server_url]}"
validation_client_name "#{@chef_config[:validation_client_name]}"
CONFIG
if !(@chef_config[:config_log_level].nil? || @chef_config[:config_log_level].empty?)
client_rb << %Q{log_level :#{@chef_config[:config_log_level]}\n}
end
client_rb << "log_location #{get_log_location}\n"
if @config[:chef_node_name]
client_rb << %Q{node_name "#{@config[:chef_node_name]}"\n}
else
client_rb << "# Using default node name (fqdn)\n"
end
# We configure :verify_api_cert only when it's overridden on the CLI
# or when specified in the knife config.
if !@config[:node_verify_api_cert].nil? || knife_config.key?(:verify_api_cert)
value = @config[:node_verify_api_cert].nil? ? knife_config[:verify_api_cert] : @config[:node_verify_api_cert]
client_rb << %Q{verify_api_cert #{value}\n}
end
# We configure :ssl_verify_mode only when it's overridden on the CLI
# or when specified in the knife config.
if @config[:node_ssl_verify_mode] || knife_config.key?(:ssl_verify_mode)
value = case @config[:node_ssl_verify_mode]
when "peer"
:verify_peer
when "none"
:verify_none
when nil
knife_config[:ssl_verify_mode]
else
nil
end
if value
client_rb << %Q{ssl_verify_mode :#{value}\n}
end
end
if @config[:ssl_verify_mode]
client_rb << %Q{ssl_verify_mode :#{knife_config[:ssl_verify_mode]}\n}
end
if knife_config[:bootstrap_proxy]
client_rb << %Q{http_proxy "#{knife_config[:bootstrap_proxy]}"\n}
client_rb << %Q{https_proxy "#{knife_config[:bootstrap_proxy]}"\n}
end
if knife_config[:bootstrap_proxy_user]
client_rb << %Q{http_proxy_user "#{knife_config[:bootstrap_proxy_user]}"\n}
client_rb << %Q{https_proxy_user "#{knife_config[:bootstrap_proxy_user]}"\n}
end
if knife_config[:bootstrap_proxy_pass]
client_rb << %Q{http_proxy_pass "#{knife_config[:bootstrap_proxy_pass]}"\n}
client_rb << %Q{https_proxy_pass "#{knife_config[:bootstrap_proxy_pass]}"\n}
end
if knife_config[:bootstrap_no_proxy]
client_rb << %Q{no_proxy "#{knife_config[:bootstrap_no_proxy]}"\n}
end
if encrypted_data_bag_secret
client_rb << %Q{encrypted_data_bag_secret "/etc/chef/encrypted_data_bag_secret"\n}
end
unless trusted_certs.empty?
client_rb << %Q{trusted_certs_dir "/etc/chef/trusted_certs"\n}
end
if Chef::Config[:fips]
client_rb << "fips true\n"
end
client_rb
end
def start_chef
# If the user doesn't have a client path configure, let bash use the PATH for what it was designed for
client_path = @chef_config[:chef_client_path] || "#{Chef::Dist::CLIENT}"
s = "CHEF_LICENSE=accept #{client_path} -j /etc/chef/first-boot.json"
if @config[:verbosity] && @config[:verbosity] >= 3
s << " -l trace"
elsif @config[:verbosity] && @config[:verbosity] >= 2
s << " -l debug"
end
s << " -E #{bootstrap_environment}" unless bootstrap_environment.nil?
s << " --no-color" unless @config[:color]
s
end
def knife_config
@chef_config.key?(:knife) ? @chef_config[:knife] : {}
end
#
# Determine that CLI arguments to retrieve the correct version of Chef Infra Client from omnitruck
# By default, bootstrap will look for the latest version of the currently-running major release of on stable.
#
# @return [String] CLI arguments to pass into the chef download helper script
def latest_current_chef_version_string
# NOTE: Changes here should also be reflected in Knife::Core::BootstrapContext#latest_current_chef_version_string
installer_version_string = []
use_current_channel = (@config[:channel] == "current")
installer_version_string << "-p" if use_current_channel
version = if knife_config[:bootstrap_version]
knife_config[:bootstrap_version]
else
# We will take the latest current by default,
# if no specific version is given.
if use_current_channel
# Take the latest stable version from the current major release.
# Note that if the current major release is not yet in stable,
# you must also specify channel "current".
nil
else
Chef::VERSION.split(".").first
end
end
installer_version_string.concat(["-v", version]) unless version.nil?
installer_version_string.join(" ")
end
def first_boot
(@config[:first_boot_attributes] || {}).tap do |attributes|
if @config[:policy_name] && @config[:policy_group]
attributes[:policy_name] = @config[:policy_name]
attributes[:policy_group] = @config[:policy_group]
else
attributes[:run_list] = @run_list
end
attributes.delete(:run_list) if attributes[:policy_name] && !attributes[:policy_name].empty?
attributes.merge!(tags: @config[:tags]) if @config[:tags] && !@config[:tags].empty?
end
end
private
# Returns a string for copying the trusted certificates on the workstation to the system being bootstrapped
# This string should contain both the commands necessary to both create the files, as well as their content
def trusted_certs_content
content = ""
if @chef_config[:trusted_certs_dir]
Dir.glob(File.join(Chef::Util::PathHelper.escape_glob_dir(@chef_config[:trusted_certs_dir]), "*.{crt,pem}")).each do |cert|
content << "cat > /etc/chef/trusted_certs/#{File.basename(cert)} <<'EOP'\n" +
IO.read(File.expand_path(cert)) + "\nEOP\n"
end
end
content
end
def client_d_content
content = ""
if @chef_config[:client_d_dir] && File.exist?(@chef_config[:client_d_dir])
root = Pathname(@chef_config[:client_d_dir])
root.find do |f|
relative = f.relative_path_from(root)
if f != root
file_on_node = "/etc/chef/client.d/#{relative}"
if f.directory?
content << "mkdir #{file_on_node}\n"
else
content << "cat > #{file_on_node} <<'EOP'\n" +
f.read.gsub("'", "'\\\\''") + "\nEOP\n"
end
end
end
end
content
end
end
end
end
end
|