knife-windows 0.8.6 → 1.0.0.rc.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|