knife-windows 0.8.6 → 1.0.0.rc.0
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/.gitignore +1 -0
- data/.travis.yml +17 -3
- data/CHANGELOG.md +25 -6
- data/DOC_CHANGES.md +323 -0
- data/Gemfile +2 -1
- data/README.md +160 -29
- data/RELEASE_NOTES.md +59 -6
- data/appveyor.yml +42 -0
- data/ci.gemfile +15 -0
- data/knife-windows.gemspec +4 -2
- data/lib/chef/knife/bootstrap/windows-chef-client-msi.erb +35 -21
- data/lib/chef/knife/bootstrap_windows_base.rb +155 -31
- data/lib/chef/knife/bootstrap_windows_ssh.rb +1 -1
- data/lib/chef/knife/bootstrap_windows_winrm.rb +17 -10
- data/lib/chef/knife/core/windows_bootstrap_context.rb +67 -16
- data/lib/chef/knife/windows_cert_generate.rb +155 -0
- data/lib/chef/knife/windows_cert_install.rb +62 -0
- data/lib/chef/knife/windows_helper.rb +3 -1
- data/lib/chef/knife/windows_listener_create.rb +100 -0
- data/lib/chef/knife/winrm.rb +84 -208
- data/lib/chef/knife/winrm_base.rb +36 -10
- data/lib/chef/knife/winrm_knife_base.rb +201 -0
- data/lib/chef/knife/winrm_session.rb +72 -0
- data/lib/chef/knife/winrm_shared_options.rb +47 -0
- data/lib/chef/knife/wsman_endpoint.rb +44 -0
- data/lib/chef/knife/wsman_test.rb +96 -0
- data/lib/knife-windows/path_helper.rb +77 -0
- data/lib/knife-windows/version.rb +1 -1
- data/spec/functional/bootstrap_download_spec.rb +41 -23
- data/spec/spec_helper.rb +11 -1
- data/spec/unit/knife/bootstrap_template_spec.rb +27 -27
- data/spec/unit/knife/bootstrap_windows_winrm_spec.rb +67 -23
- data/spec/unit/knife/core/windows_bootstrap_context_spec.rb +47 -0
- data/spec/unit/knife/windows_cert_generate_spec.rb +90 -0
- data/spec/unit/knife/windows_cert_install_spec.rb +35 -0
- data/spec/unit/knife/windows_listener_create_spec.rb +61 -0
- data/spec/unit/knife/winrm_session_spec.rb +47 -0
- data/spec/unit/knife/winrm_spec.rb +222 -56
- data/spec/unit/knife/wsman_test_spec.rb +176 -0
- metadata +51 -20
@@ -19,7 +19,8 @@
|
|
19
19
|
require 'chef/knife/bootstrap_windows_base'
|
20
20
|
require 'chef/knife/winrm'
|
21
21
|
require 'chef/knife/winrm_base'
|
22
|
-
require 'chef/knife/
|
22
|
+
require 'chef/knife/winrm_knife_base'
|
23
|
+
|
23
24
|
|
24
25
|
class Chef
|
25
26
|
class Knife
|
@@ -27,6 +28,7 @@ class Chef
|
|
27
28
|
|
28
29
|
include Chef::Knife::BootstrapWindowsBase
|
29
30
|
include Chef::Knife::WinrmBase
|
31
|
+
include Chef::Knife::WinrmCommandSharedFunctions
|
30
32
|
|
31
33
|
deps do
|
32
34
|
require 'chef/knife/core/windows_bootstrap_context'
|
@@ -38,27 +40,33 @@ class Chef
|
|
38
40
|
banner "knife bootstrap windows winrm FQDN (options)"
|
39
41
|
|
40
42
|
def run
|
43
|
+
if (Chef::Config[:validation_key] && !File.exist?(File.expand_path(Chef::Config[:validation_key])))
|
44
|
+
if !negotiate_auth? && !(locate_config_value(:winrm_transport) == 'ssl')
|
45
|
+
ui.error("Validatorless bootstrap over unsecure winrm channels could expose your key to network sniffing")
|
46
|
+
exit 1
|
47
|
+
end
|
48
|
+
end
|
41
49
|
bootstrap
|
42
50
|
end
|
43
51
|
|
44
|
-
|
45
52
|
def run_command(command = '')
|
46
53
|
winrm = Chef::Knife::Winrm.new
|
47
54
|
winrm.name_args = [ server_name, command ]
|
48
55
|
winrm.config[:winrm_user] = locate_config_value(:winrm_user)
|
49
56
|
winrm.config[:winrm_password] = locate_config_value(:winrm_password)
|
50
57
|
winrm.config[:winrm_transport] = locate_config_value(:winrm_transport)
|
51
|
-
winrm.config[:
|
52
|
-
winrm.config[:
|
53
|
-
winrm.config[:
|
54
|
-
winrm.config[:
|
58
|
+
winrm.config[:winrm_ssl_verify_mode] = locate_config_value(:winrm_ssl_verify_mode)
|
59
|
+
winrm.config[:kerberos_keytab_file] = locate_config_value(:kerberos_keytab_file) if locate_config_value(:kerberos_keytab_file)
|
60
|
+
winrm.config[:kerberos_realm] = locate_config_value(:kerberos_realm) if locate_config_value(:kerberos_realm)
|
61
|
+
winrm.config[:kerberos_service] = locate_config_value(:kerberos_service) if locate_config_value(:kerberos_service)
|
62
|
+
winrm.config[:ca_trust_file] = locate_config_value(:ca_trust_file) if locate_config_value(:ca_trust_file)
|
55
63
|
winrm.config[:manual] = true
|
56
64
|
winrm.config[:winrm_port] = locate_config_value(:winrm_port)
|
57
65
|
winrm.config[:suppress_auth_failure] = true
|
58
|
-
|
59
|
-
#If you turn off the return flag, then winrm.run won't atually check and
|
66
|
+
|
67
|
+
#If you turn off the return flag, then winrm.run won't atually check and
|
60
68
|
#return the error
|
61
|
-
#codes. Otherwise, it ignores the return value of the server call.
|
69
|
+
#codes. Otherwise, it ignores the return value of the server call.
|
62
70
|
winrm.config[:returns] = "0"
|
63
71
|
winrm.run
|
64
72
|
end
|
@@ -99,4 +107,3 @@ class Chef
|
|
99
107
|
end
|
100
108
|
end
|
101
109
|
end
|
102
|
-
|
@@ -22,6 +22,7 @@ require 'chef/knife/core/bootstrap_context'
|
|
22
22
|
require 'knife-windows/path_helper'
|
23
23
|
# require 'chef/util/path_helper'
|
24
24
|
|
25
|
+
|
25
26
|
class Chef
|
26
27
|
class Knife
|
27
28
|
module Core
|
@@ -34,27 +35,37 @@ class Chef
|
|
34
35
|
class WindowsBootstrapContext < BootstrapContext
|
35
36
|
PathHelper = ::Knife::Windows::PathHelper
|
36
37
|
|
37
|
-
|
38
|
+
attr_accessor :client_pem
|
39
|
+
|
40
|
+
def initialize(config, run_list, chef_config, secret=nil)
|
38
41
|
@config = config
|
39
42
|
@run_list = run_list
|
40
43
|
@chef_config = chef_config
|
41
|
-
|
44
|
+
@secret = secret
|
45
|
+
# Compatibility with Chef 12 and Chef 11 versions
|
46
|
+
begin
|
47
|
+
# Pass along the secret parameter for Chef 12
|
48
|
+
super(config, run_list, chef_config, secret)
|
49
|
+
rescue ArgumentError
|
50
|
+
# The Chef 11 base class only has parameters for initialize
|
51
|
+
super(config, run_list, chef_config)
|
52
|
+
end
|
42
53
|
end
|
43
54
|
|
44
55
|
def validation_key
|
45
|
-
if
|
46
|
-
|
56
|
+
if File.exist?(File.expand_path(@chef_config[:validation_key]))
|
57
|
+
IO.read(File.expand_path(@chef_config[:validation_key]))
|
47
58
|
else
|
48
|
-
|
59
|
+
false
|
49
60
|
end
|
50
61
|
end
|
51
62
|
|
52
|
-
def
|
53
|
-
escape_and_echo(@config[:
|
63
|
+
def secret
|
64
|
+
escape_and_echo(@config[:secret])
|
54
65
|
end
|
55
66
|
|
56
|
-
def
|
57
|
-
@
|
67
|
+
def trusted_certs_script
|
68
|
+
@trusted_certs_script ||= trusted_certs_content
|
58
69
|
end
|
59
70
|
|
60
71
|
def config_content
|
@@ -64,8 +75,6 @@ log_location STDOUT
|
|
64
75
|
|
65
76
|
chef_server_url "#{@chef_config[:chef_server_url]}"
|
66
77
|
validation_client_name "#{@chef_config[:validation_client_name]}"
|
67
|
-
client_key "c:/chef/client.pem"
|
68
|
-
validation_key "c:/chef/validation.pem"
|
69
78
|
|
70
79
|
file_cache_path "c:/chef/cache"
|
71
80
|
file_backup_path "c:/chef/backup"
|
@@ -119,12 +128,12 @@ CONFIG
|
|
119
128
|
client_rb << %Q{no_proxy "#{knife_config[:bootstrap_no_proxy]}"\n}
|
120
129
|
end
|
121
130
|
|
122
|
-
if @config[:
|
131
|
+
if @config[:secret]
|
123
132
|
client_rb << %Q{encrypted_data_bag_secret "c:/chef/encrypted_data_bag_secret"\n}
|
124
133
|
end
|
125
134
|
|
126
|
-
unless
|
127
|
-
client_rb << %Q{trusted_certs_dir "
|
135
|
+
unless trusted_certs_script.empty?
|
136
|
+
client_rb << %Q{trusted_certs_dir "c:/chef/trusted_certs"\n}
|
128
137
|
end
|
129
138
|
|
130
139
|
escape_and_echo(client_rb)
|
@@ -158,10 +167,30 @@ CONFIG
|
|
158
167
|
end
|
159
168
|
|
160
169
|
def win_wget
|
170
|
+
# I tried my best to figure out how to properly url decode and switch / to \
|
171
|
+
# but this is VBScript - so I don't really care that badly.
|
161
172
|
win_wget = <<-WGET
|
162
173
|
url = WScript.Arguments.Named("url")
|
163
174
|
path = WScript.Arguments.Named("path")
|
164
175
|
proxy = null
|
176
|
+
'* Vaguely attempt to handle file:// scheme urls by url unescaping and switching all
|
177
|
+
'* / into \. Also assume that file:/// is a local absolute path and that file://<foo>
|
178
|
+
'* is possibly a network file path.
|
179
|
+
If InStr(url, "file://") = 1 Then
|
180
|
+
url = Unescape(url)
|
181
|
+
If InStr(url, "file:///") = 1 Then
|
182
|
+
sourcePath = Mid(url, Len("file:///") + 1)
|
183
|
+
Else
|
184
|
+
sourcePath = Mid(url, Len("file:") + 1)
|
185
|
+
End If
|
186
|
+
sourcePath = Replace(sourcePath, "/", "\\")
|
187
|
+
|
188
|
+
Set objFSO = CreateObject("Scripting.FileSystemObject")
|
189
|
+
If objFSO.Fileexists(path) Then objFSO.DeleteFile path
|
190
|
+
objFSO.CopyFile sourcePath, path, true
|
191
|
+
Set objFSO = Nothing
|
192
|
+
|
193
|
+
Else
|
165
194
|
Set objXMLHTTP = CreateObject("MSXML2.ServerXMLHTTP")
|
166
195
|
Set wshShell = CreateObject( "WScript.Shell" )
|
167
196
|
Set objUserVariables = wshShell.Environment("USER")
|
@@ -200,8 +229,9 @@ Set objFSO = Nothing
|
|
200
229
|
objADOStream.SaveToFile path
|
201
230
|
objADOStream.Close
|
202
231
|
Set objADOStream = Nothing
|
203
|
-
End
|
232
|
+
End If
|
204
233
|
Set objXMLHTTP = Nothing
|
234
|
+
End If
|
205
235
|
WGET
|
206
236
|
escape_and_echo(win_wget)
|
207
237
|
end
|
@@ -235,6 +265,21 @@ WGET_PS
|
|
235
265
|
local_download_path = "%TEMP%\\chef-client-latest.msi"
|
236
266
|
end
|
237
267
|
|
268
|
+
def msi_url(machine_os=nil, machine_arch=nil, download_context=nil)
|
269
|
+
# The default msi path has a number of url query parameters - we attempt to substitute
|
270
|
+
# such parameters in as long as they are provided by the template.
|
271
|
+
|
272
|
+
if @config[:msi_url].nil? || @config[:msi_url].empty?
|
273
|
+
url = "https://www.chef.io/chef/download?p=windows"
|
274
|
+
url += "&pv=#{machine_os}" unless machine_os.nil?
|
275
|
+
url += "&m=#{machine_arch}" unless machine_arch.nil?
|
276
|
+
url += "&DownloadContext=#{download_context}" unless download_context.nil?
|
277
|
+
url += latest_current_windows_chef_version_query
|
278
|
+
else
|
279
|
+
@config[:msi_url]
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
238
283
|
def first_boot
|
239
284
|
first_boot_attributes_and_run_list = (@config[:first_boot_attributes] || {}).merge(:run_list => @run_list)
|
240
285
|
escape_and_echo(first_boot_attributes_and_run_list.to_json)
|
@@ -250,9 +295,15 @@ WGET_PS
|
|
250
295
|
private
|
251
296
|
|
252
297
|
def install_command(executor_quote)
|
253
|
-
|
298
|
+
if @config[:install_as_service]
|
299
|
+
"msiexec /qn /log #{executor_quote}%CHEF_CLIENT_MSI_LOG_PATH%#{executor_quote} /i #{executor_quote}%LOCAL_DESTINATION_MSI_PATH%#{executor_quote} ADDLOCAL=#{executor_quote}ChefClientFeature,ChefServiceFeature#{executor_quote}"
|
300
|
+
else
|
301
|
+
"msiexec /qn /log #{executor_quote}%CHEF_CLIENT_MSI_LOG_PATH%#{executor_quote} /i #{executor_quote}%LOCAL_DESTINATION_MSI_PATH%#{executor_quote}"
|
302
|
+
end
|
254
303
|
end
|
255
304
|
|
305
|
+
# Returns a string for copying the trusted certificates on the workstation to the system being bootstrapped
|
306
|
+
# This string should contain both the commands necessary to both create the files, as well as their content
|
256
307
|
def trusted_certs_content
|
257
308
|
content = ""
|
258
309
|
if @chef_config[:trusted_certs_dir]
|
@@ -0,0 +1,155 @@
|
|
1
|
+
# Author:: Mukta Aphale (<mukta.aphale@clogeny.com>)
|
2
|
+
# Copyright:: Copyright (c) 2014 Opscode, Inc.
|
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 'chef/knife'
|
19
|
+
require 'chef/knife/winrm_base'
|
20
|
+
require 'openssl'
|
21
|
+
require 'socket'
|
22
|
+
|
23
|
+
class Chef
|
24
|
+
class Knife
|
25
|
+
class WindowsCertGenerate < Knife
|
26
|
+
|
27
|
+
attr_accessor :thumbprint, :hostname
|
28
|
+
|
29
|
+
banner "knife windows cert generate FILE_PATH (options)"
|
30
|
+
|
31
|
+
option :hostname,
|
32
|
+
:short => "-H HOSTNAME",
|
33
|
+
:long => "--hostname HOSTNAME",
|
34
|
+
:description => "Use to specify the hostname for the listener.
|
35
|
+
For example, --hostname something.mydomain.com or *.mydomain.com.",
|
36
|
+
:required => true
|
37
|
+
|
38
|
+
option :output_file,
|
39
|
+
:short => "-o PATH",
|
40
|
+
:long => "--output-file PATH",
|
41
|
+
:description => "Specifies the file path at which to generate the 3 certificate files of type .pfx, .b64, and .pem. The default is './winrmcert'.",
|
42
|
+
:default => "winrmcert"
|
43
|
+
|
44
|
+
option :key_length,
|
45
|
+
:short => "-k LENGTH",
|
46
|
+
:long => "--key-length LENGTH",
|
47
|
+
:description => "Default is 2048",
|
48
|
+
:default => "2048"
|
49
|
+
|
50
|
+
option :cert_validity,
|
51
|
+
:short => "-cv MONTHS",
|
52
|
+
:long => "--cert-validity MONTHS",
|
53
|
+
:description => "Default is 24 months",
|
54
|
+
:default => "24"
|
55
|
+
|
56
|
+
option :cert_passphrase,
|
57
|
+
:short => "-cp PASSWORD",
|
58
|
+
:long => "--cert-passphrase PASSWORD",
|
59
|
+
:description => "Password for certificate."
|
60
|
+
|
61
|
+
def generate_keypair
|
62
|
+
OpenSSL::PKey::RSA.new(config[:key_length].to_i)
|
63
|
+
end
|
64
|
+
|
65
|
+
def prompt_for_passphrase
|
66
|
+
passphrase = ""
|
67
|
+
begin
|
68
|
+
print "Passphrases do not match. Try again.\n" unless passphrase.empty?
|
69
|
+
print "Enter certificate passphrase (empty for no passphrase):"
|
70
|
+
passphrase = STDIN.gets
|
71
|
+
return passphrase.strip if passphrase == "\n"
|
72
|
+
print "Enter same passphrase again:"
|
73
|
+
confirm_passphrase = STDIN.gets
|
74
|
+
end until passphrase == confirm_passphrase
|
75
|
+
passphrase.strip
|
76
|
+
end
|
77
|
+
|
78
|
+
def generate_certificate rsa_key
|
79
|
+
@hostname = config[:hostname] if config[:hostname]
|
80
|
+
|
81
|
+
#Create a self-signed X509 certificate from the rsa_key (unencrypted)
|
82
|
+
cert = OpenSSL::X509::Certificate.new
|
83
|
+
cert.version = 2
|
84
|
+
cert.serial = Random.rand(65534) + 1 # 2 digit byte range random number for better security aspect
|
85
|
+
|
86
|
+
cert.subject = OpenSSL::X509::Name.parse "/CN=#{@hostname}"
|
87
|
+
cert.issuer = cert.subject
|
88
|
+
cert.public_key = rsa_key.public_key
|
89
|
+
cert.not_before = Time.now
|
90
|
+
cert.not_after = cert.not_before + 2 * 365 * config[:cert_validity].to_i * 60 * 60 # 2 years validity
|
91
|
+
ef = OpenSSL::X509::ExtensionFactory.new
|
92
|
+
ef.subject_certificate = cert
|
93
|
+
ef.issuer_certificate = cert
|
94
|
+
cert.add_extension(ef.create_extension("subjectKeyIdentifier","hash",false))
|
95
|
+
cert.add_extension(ef.create_extension("authorityKeyIdentifier","keyid:always",false))
|
96
|
+
cert.add_extension(ef.create_extension("extendedKeyUsage", "1.3.6.1.5.5.7.3.1", false))
|
97
|
+
cert.sign(rsa_key, OpenSSL::Digest::SHA1.new)
|
98
|
+
@thumbprint = OpenSSL::Digest::SHA1.new(cert.to_der)
|
99
|
+
cert
|
100
|
+
end
|
101
|
+
|
102
|
+
def write_certificate_to_file(cert, file_path, rsa_key)
|
103
|
+
File.open(file_path + ".pem", "wb") { |f| f.print cert.to_pem }
|
104
|
+
config[:cert_passphrase] = prompt_for_passphrase unless config[:cert_passphrase]
|
105
|
+
pfx = OpenSSL::PKCS12.create("#{config[:cert_passphrase]}", "winrmcert", rsa_key, cert)
|
106
|
+
File.open(file_path + ".pfx", "wb") { |f| f.print pfx.to_der }
|
107
|
+
File.open(file_path + ".b64", "wb") { |f| f.print Base64.strict_encode64(pfx.to_der) }
|
108
|
+
end
|
109
|
+
|
110
|
+
def certificates_already_exist?(file_path)
|
111
|
+
certs_exists = false
|
112
|
+
%w{pem pfx b64}.each do |extn|
|
113
|
+
if !Dir.glob("#{file_path}.*#{extn}").empty?
|
114
|
+
certs_exists = true
|
115
|
+
break
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
if certs_exists
|
120
|
+
begin
|
121
|
+
confirm("Do you really want to overwrite existing certificates")
|
122
|
+
rescue SystemExit # Need to handle this as confirming with N/n raises SystemExit exception
|
123
|
+
exit!
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def run
|
129
|
+
STDOUT.sync = STDERR.sync = true
|
130
|
+
|
131
|
+
# takes user specified first cli value as a destination file path for generated cert.
|
132
|
+
file_path = @name_args.empty? ? config[:output_file].sub(/\.(\w+)$/,'') : @name_args.first
|
133
|
+
|
134
|
+
# check if certs already exists at given file path
|
135
|
+
certificates_already_exist? file_path
|
136
|
+
|
137
|
+
begin
|
138
|
+
filename = File.basename(file_path)
|
139
|
+
rsa_key = generate_keypair
|
140
|
+
cert = generate_certificate rsa_key
|
141
|
+
write_certificate_to_file cert, file_path, rsa_key
|
142
|
+
ui.info "Generated Certificates:"
|
143
|
+
ui.info "- #{filename}.pfx - PKCS12 format key pair. Contains public and private keys, can be used with an SSL server."
|
144
|
+
ui.info "- #{filename}.b64 - Base64 encoded PKCS12 key pair. Contains public and private keys, used by some cloud provider API's to configure SSL servers."
|
145
|
+
ui.info "- #{filename}.pem - Base64 encoded public certificate only. Required by the client to connect to the server."
|
146
|
+
ui.info "Certificate Thumbprint: #{@thumbprint.to_s.upcase}"
|
147
|
+
rescue => e
|
148
|
+
puts "ERROR: + #{e}"
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# Author:: Mukta Aphale (<mukta.aphale@clogeny.com>)
|
2
|
+
# Copyright:: Copyright (c) 2014 Opscode, Inc.
|
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 'chef/knife'
|
19
|
+
require 'chef/knife/winrm_base'
|
20
|
+
|
21
|
+
class Chef
|
22
|
+
class Knife
|
23
|
+
class WindowsCertInstall < Knife
|
24
|
+
|
25
|
+
banner "knife windows cert install CERT [CERT] (options)"
|
26
|
+
|
27
|
+
option :cert_passphrase,
|
28
|
+
:short => "-cp PASSWORD",
|
29
|
+
:long => "--cert-passphrase PASSWORD",
|
30
|
+
:description => "Password for certificate."
|
31
|
+
|
32
|
+
def get_cert_passphrase
|
33
|
+
print "Enter given certificate's passphrase (empty for no passphrase):"
|
34
|
+
passphrase = STDIN.gets
|
35
|
+
passphrase.strip
|
36
|
+
end
|
37
|
+
|
38
|
+
def run
|
39
|
+
STDOUT.sync = STDERR.sync = true
|
40
|
+
if @name_args.empty?
|
41
|
+
ui.error "Please specify the certificate path. e.g- 'knife windows cert install <path>"
|
42
|
+
exit 1
|
43
|
+
end
|
44
|
+
file_path = @name_args.first
|
45
|
+
config[:cert_passphrase] = get_cert_passphrase unless config[:cert_passphrase]
|
46
|
+
|
47
|
+
begin
|
48
|
+
ui.info "Adding certificate to the Windows Certificate Store..."
|
49
|
+
result = %x{powershell.exe -Command " '#{config[:cert_passphrase]}' | certutil -importPFX '#{file_path}' AT_KEYEXCHANGE"}
|
50
|
+
if $?.exitstatus == 0
|
51
|
+
ui.info "Certificate added to Certificate Store"
|
52
|
+
else
|
53
|
+
ui.info "Error adding the certificate. Use -VV option for details"
|
54
|
+
end
|
55
|
+
Chef::Log.debug "#{result}"
|
56
|
+
rescue => e
|
57
|
+
puts "ERROR: + #{e}"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|