knife-azure 3.0.0 → 4.0.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.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/lib/azure/custom_errors.rb +1 -1
  3. data/lib/azure/resource_management/ARM_deployment_template.rb +5 -5
  4. data/lib/azure/resource_management/ARM_interface.rb +8 -12
  5. data/lib/azure/resource_management/windows_credentials.rb +7 -8
  6. data/lib/chef/knife/azurerm_server_create.rb +2 -2
  7. data/lib/chef/knife/azurerm_server_delete.rb +1 -1
  8. data/lib/chef/knife/bootstrap/bootstrapper.rb +10 -11
  9. data/lib/chef/knife/bootstrap_azurerm.rb +1 -1
  10. data/lib/chef/knife/helpers/azurerm_base.rb +17 -19
  11. data/lib/knife-azure/version.rb +1 -1
  12. metadata +30 -43
  13. data/lib/azure/service_management/ASM_interface.rb +0 -310
  14. data/lib/azure/service_management/ag.rb +0 -99
  15. data/lib/azure/service_management/certificate.rb +0 -235
  16. data/lib/azure/service_management/connection.rb +0 -102
  17. data/lib/azure/service_management/deploy.rb +0 -221
  18. data/lib/azure/service_management/disk.rb +0 -68
  19. data/lib/azure/service_management/host.rb +0 -184
  20. data/lib/azure/service_management/image.rb +0 -94
  21. data/lib/azure/service_management/loadbalancer.rb +0 -78
  22. data/lib/azure/service_management/rest.rb +0 -125
  23. data/lib/azure/service_management/role.rb +0 -717
  24. data/lib/azure/service_management/storageaccount.rb +0 -127
  25. data/lib/azure/service_management/utility.rb +0 -40
  26. data/lib/azure/service_management/vnet.rb +0 -134
  27. data/lib/chef/knife/azure_ag_create.rb +0 -73
  28. data/lib/chef/knife/azure_ag_list.rb +0 -35
  29. data/lib/chef/knife/azure_image_list.rb +0 -56
  30. data/lib/chef/knife/azure_internal-lb_create.rb +0 -74
  31. data/lib/chef/knife/azure_internal-lb_list.rb +0 -35
  32. data/lib/chef/knife/azure_server_create.rb +0 -531
  33. data/lib/chef/knife/azure_server_delete.rb +0 -136
  34. data/lib/chef/knife/azure_server_list.rb +0 -38
  35. data/lib/chef/knife/azure_server_show.rb +0 -41
  36. data/lib/chef/knife/azure_vnet_create.rb +0 -74
  37. data/lib/chef/knife/azure_vnet_list.rb +0 -35
  38. data/lib/chef/knife/bootstrap_azure.rb +0 -191
  39. data/lib/chef/knife/helpers/azure_base.rb +0 -394
@@ -1,310 +0,0 @@
1
- #
2
- # Copyright:: Copyright (c) Chef Software 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_relative "../azure_interface"
19
- require_relative "rest"
20
- require_relative "connection"
21
-
22
- module Azure
23
- class ServiceManagement
24
- class ASMInterface < AzureInterface
25
- include AzureAPI
26
-
27
- attr_accessor :connection
28
-
29
- def initialize(params = {})
30
- @rest = Rest.new(params)
31
- @connection = Azure::ServiceManagement::Connection.new(@rest)
32
- super
33
- end
34
-
35
- def list_images
36
- connection.images.all
37
- end
38
-
39
- def list_servers
40
- servers = connection.roles.all
41
- cols = ["DNS Name", "VM Name", "Status", "IP Address", "SSH Port", "WinRM Port", "RDP Port"]
42
- rows = []
43
- servers.each do |server|
44
- rows << server.hostedservicename.to_s + ".cloudapp.net" # Info about the DNS name at http://msdn.microsoft.com/en-us/library/ee460806.aspx
45
- rows << server.name.to_s
46
- rows << begin
47
- state = server.status.to_s.downcase
48
- case state
49
- when "shutting-down", "terminated", "stopping", "stopped"
50
- ui.color(state, :red)
51
- when "pending"
52
- ui.color(state, :yellow)
53
- else
54
- ui.color("ready", :green)
55
- end
56
- end
57
- rows << server.publicipaddress.to_s
58
- rows << server.sshport.to_s
59
- rows << server.winrmport.to_s
60
- ports = server.tcpports
61
- rows << rdp_port(ports)
62
- end
63
- display_list(ui, cols, rows)
64
- end
65
-
66
- def rdp_port(arr_ports)
67
- unless arr_ports
68
- return ""
69
- end
70
-
71
- if arr_ports.length > 0
72
- arr_ports.each do |port|
73
- if port["Name"] == "Remote Desktop"
74
- return port["PublicPort"]
75
- end
76
- end
77
- end
78
- ""
79
- end
80
-
81
- def find_server(params = {})
82
- server = connection.roles.find(params[:name], params = { azure_dns_name: params[:azure_dns_name] })
83
- end
84
-
85
- def delete_server(params = {})
86
- server = find_server({ name: params[:name], azure_dns_name: params[:azure_dns_name] })
87
-
88
- unless server
89
- puts "\n"
90
- ui.error("Server #{params[:name]} does not exist")
91
- exit!
92
- end
93
-
94
- puts "\n"
95
- msg_pair(ui, "DNS Name", server.hostedservicename + ".cloudapp.net")
96
- msg_pair(ui, "VM Name", server.name)
97
- msg_pair(ui, "Size", server.size)
98
- msg_pair(ui, "Public Ip Address", server.publicipaddress)
99
- puts "\n"
100
-
101
- begin
102
- ui.confirm("Do you really want to delete this server")
103
- rescue SystemExit # Need to handle this as confirming with N/n raises SystemExit exception
104
- server = nil # Cleanup is implicitly performed in other cloud plugins
105
- exit!
106
- end
107
-
108
- params[:azure_dns_name] = server.hostedservicename
109
-
110
- connection.roles.delete(params)
111
-
112
- puts "\n"
113
- ui.warn("Deleted server #{server.name}")
114
- end
115
-
116
- def show_server(name)
117
- role = connection.roles.find name
118
-
119
- puts ""
120
- if role
121
- details = []
122
- details << ui.color("Role name", :bold, :cyan)
123
- details << role.name
124
- details << ui.color("Status", :bold, :cyan)
125
- details << role.status
126
- details << ui.color("Size", :bold, :cyan)
127
- details << role.size
128
- details << ui.color("Hosted service name", :bold, :cyan)
129
- details << role.hostedservicename
130
- details << ui.color("Deployment name", :bold, :cyan)
131
- details << role.deployname
132
- details << ui.color("Host name", :bold, :cyan)
133
- details << role.hostname
134
- unless role.sshport.nil?
135
- details << ui.color("SSH port", :bold, :cyan)
136
- details << role.sshport
137
- end
138
- unless role.winrmport.nil?
139
- details << ui.color("WinRM port", :bold, :cyan)
140
- details << role.winrmport
141
- end
142
- details << ui.color("Public IP", :bold, :cyan)
143
- details << role.publicipaddress.to_s
144
-
145
- unless role.thumbprint.empty?
146
- details << ui.color("Thumbprint", :bold, :cyan)
147
- details << role.thumbprint
148
- end
149
- puts ui.list(details, :columns_across, 2)
150
- if role.tcpports.length > 0 || role.udpports.length > 0
151
- details.clear
152
- details << ui.color("Ports open", :bold, :cyan)
153
- details << ui.color("Local port", :bold, :cyan)
154
- details << ui.color("IP", :bold, :cyan)
155
- details << ui.color("Public port", :bold, :cyan)
156
- if role.tcpports.length > 0
157
- role.tcpports.each do |port|
158
- details << "tcp"
159
- details << port["LocalPort"]
160
- details << port["Vip"]
161
- details << port["PublicPort"]
162
- end
163
- end
164
- if role.udpports.length > 0
165
- role.udpports.each do |port|
166
- details << "udp"
167
- details << port["LocalPort"]
168
- details << port["Vip"]
169
- details << port["PublicPort"]
170
- end
171
- end
172
- puts ui.list(details, :columns_across, 4)
173
- end
174
- else
175
- puts "No VM found"
176
- end
177
-
178
- rescue => error
179
- puts "#{error.class} and #{error.message}"
180
- end
181
-
182
- def list_internal_lb
183
- lbs = connection.lbs.all
184
- cols = %w{Name Service Subnet VIP}
185
- rows = []
186
- lbs.each do |lb|
187
- cols.each { |col| rows << lb.send(col.downcase).to_s }
188
- end
189
- display_list(ui, cols, rows)
190
- end
191
-
192
- def create_internal_lb(params = {})
193
- connection.lbs.create(params)
194
- end
195
-
196
- def list_vnets
197
- vnets = connection.vnets.all
198
- cols = ["Name", "Affinity Group", "State"]
199
- rows = []
200
- vnets.each do |vnet|
201
- %w{name affinity_group state}.each { |col| rows << vnet.send(col).to_s }
202
- end
203
- display_list(ui, cols, rows)
204
- end
205
-
206
- def create_vnet(params = {})
207
- connection.vnets.create(params)
208
- end
209
-
210
- def list_affinity_groups
211
- affinity_groups = connection.ags.all
212
- cols = %w{Name Location Description}
213
- rows = []
214
- affinity_groups.each do |affinity_group|
215
- cols.each { |col| rows << affinity_group.send(col.downcase).to_s }
216
- end
217
- display_list(ui, cols, rows)
218
- end
219
-
220
- def create_affinity_group(params = {})
221
- connection.ags.create(params)
222
- end
223
-
224
- def create_server(params = {})
225
- remove_hosted_service_on_failure = params[:azure_dns_name]
226
- if connection.hosts.exists?(params[:azure_dns_name])
227
- remove_hosted_service_on_failure = nil
228
- end
229
-
230
- # If Storage Account is not specified, check if the geographic location has one to re-use
231
- if not params[:azure_storage_account]
232
- storage_accts = connection.storageaccounts.all
233
- storage = storage_accts.find { |storage_acct| storage_acct.location.to_s == params[:azure_service_location] }
234
- unless storage
235
- params[:azure_storage_account] = [strip_non_ascii(params[:azure_vm_name]), random_string].join.downcase
236
- remove_storage_service_on_failure = params[:azure_storage_account]
237
- else
238
- remove_storage_service_on_failure = nil
239
- params[:azure_storage_account] = storage.name.to_s
240
- end
241
- else
242
- if connection.storageaccounts.exists?(params[:azure_storage_account])
243
- remove_storage_service_on_failure = nil
244
- else
245
- remove_storage_service_on_failure = params[:azure_storage_account]
246
- end
247
- end
248
-
249
- begin
250
- connection.deploys.create(params)
251
- rescue Exception => e
252
- Chef::Log.error("Failed to create the server -- exception being rescued: #{e}")
253
- backtrace_message = "#{e.class}: #{e}\n#{e.backtrace.join("\n")}"
254
- Chef::Log.debug("#{backtrace_message}")
255
- cleanup_and_exit(remove_hosted_service_on_failure, remove_storage_service_on_failure)
256
- end
257
- end
258
-
259
- def cleanup_and_exit(remove_hosted_service_on_failure, remove_storage_service_on_failure)
260
- Chef::Log.warn("Cleaning up resources...")
261
-
262
- if remove_hosted_service_on_failure
263
- ret_val = connection.hosts.delete(remove_hosted_service_on_failure)
264
- ret_val.content.empty? ? Chef::Log.warn("Deleted created DNS: #{remove_hosted_service_on_failure}.") : Chef::Log.warn("Deletion failed for created DNS:#{remove_hosted_service_on_failure}. " + ret_val.text)
265
- end
266
-
267
- if remove_storage_service_on_failure
268
- ret_val = connection.storageaccounts.delete(remove_storage_service_on_failure)
269
- ret_val.content.empty? ? Chef::Log.warn("Deleted created Storage Account: #{remove_storage_service_on_failure}.") : Chef::Log.warn("Deletion failed for created Storage Account: #{remove_storage_service_on_failure}. " + ret_val.text)
270
- end
271
- exit 1
272
- end
273
-
274
- def get_role_server(dns_name, vm_name)
275
- deploy = connection.deploys.queryDeploy(dns_name)
276
- deploy.find_role(vm_name)
277
- end
278
-
279
- def get_extension(name, publisher)
280
- connection.query_azure("resourceextensions/#{publisher}/#{name}")
281
- end
282
-
283
- def deployment_name(dns_name)
284
- connection.deploys.get_deploy_name_for_hostedservice(dns_name)
285
- end
286
-
287
- def deployment(path)
288
- connection.query_azure(path)
289
- end
290
-
291
- def valid_image?(name)
292
- connection.images.exists?(name)
293
- end
294
-
295
- def vm_image?(name)
296
- connection.images.is_vm_image(name)
297
- end
298
-
299
- def add_extension(name, params = {})
300
- ui.info "Started with Chef Extension deployment on the server #{name}..."
301
- connection.roles.update(name, params)
302
- ui.info "\nSuccessfully deployed Chef Extension on the server #{name}."
303
- rescue Exception => e
304
- Chef::Log.error("Failed to add extension to the server -- exception being rescued: #{e}")
305
- backtrace_message = "#{e.class}: #{e}\n#{e.backtrace.join("\n")}"
306
- Chef::Log.debug("#{backtrace_message}")
307
- end
308
- end
309
- end
310
- end
@@ -1,99 +0,0 @@
1
- #
2
- # Author:: Jeff Mendoza (jeffmendoza@live.com)
3
- # Copyright:: Copyright (c) Chef Software Inc.
4
- # License:: Apache License, Version 2.0
5
- #
6
- # Licensed under the Apache License, Version 2.0 (the "License");
7
- # you may not use this file except in compliance with the License.
8
- # You may obtain a copy of the License at
9
- #
10
- # http://www.apache.org/licenses/LICENSE-2.0
11
- #
12
- # Unless required by applicable law or agreed to in writing, software
13
- # distributed under the License is distributed on an "AS IS" BASIS,
14
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
- # See the License for the specific language governing permissions and
16
- # limitations under the License.
17
- #
18
-
19
- module Azure
20
- class AGs
21
- def initialize(connection)
22
- @connection = connection
23
- end
24
-
25
- def load
26
- @ags ||= begin
27
- @ags = {}
28
- response = @connection.query_azure("affinitygroups",
29
- "get",
30
- "",
31
- "",
32
- true,
33
- false)
34
- response.css("AffinityGroup").each do |ag|
35
- item = AG.new(@connection).parse(ag)
36
- @ags[item.name] = item
37
- end
38
- @ags
39
- end
40
- end
41
-
42
- def all
43
- load.values
44
- end
45
-
46
- def exists?(name)
47
- load.key?(name)
48
- end
49
-
50
- def find(name)
51
- load[name]
52
- end
53
-
54
- def create(params)
55
- ag = AG.new(@connection)
56
- ag.create(params)
57
- end
58
- end
59
- end
60
-
61
- module Azure
62
- class AG
63
- attr_accessor :name, :label, :description, :location
64
-
65
- def initialize(connection)
66
- @connection = connection
67
- end
68
-
69
- def parse(image)
70
- @name = image.at_css("Name").content
71
- @label = image.at_css("Label").content
72
- @description = image.at_css("Description").content if
73
- image.at_css("Description")
74
- @location = image.at_css("Location").content if image.at_css("Location")
75
- self
76
- end
77
-
78
- def create(params)
79
- builder = Nokogiri::XML::Builder.new(encoding: "utf-8") do |xml|
80
- xml.CreateAffinityGroup(
81
- xmlns: "http://schemas.microsoft.com/windowsazure"
82
- ) do
83
- xml.Name params[:azure_ag_name]
84
- xml.Label Base64.strict_encode64(params[:azure_ag_name])
85
- unless params[:azure_ag_desc].nil?
86
- xml.Description params[:azure_ag_desc]
87
- end
88
- xml.Location params[:azure_location]
89
- end
90
- end
91
- @connection.query_azure("affinitygroups",
92
- "post",
93
- builder.to_xml,
94
- "",
95
- true,
96
- false)
97
- end
98
- end
99
- end
@@ -1,235 +0,0 @@
1
- #
2
- # Author:: Mukta Aphale (mukta.aphale@clogeny.com)
3
- # Copyright:: Copyright (c) Chef Software Inc.
4
- # License:: Apache License, Version 2.0
5
- #
6
- # Licensed under the Apache License, Version 2.0 (the "License");
7
- # you may not use this file except in compliance with the License.
8
- # You may obtain a copy of the License at
9
- #
10
- # http://www.apache.org/licenses/LICENSE-2.0
11
- #
12
- # Unless required by applicable law or agreed to in writing, software
13
- # distributed under the License is distributed on an "AS IS" BASIS,
14
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
- # See the License for the specific language governing permissions and
16
- # limitations under the License.
17
- #
18
-
19
- module Azure
20
- class Certificates
21
- def initialize(connection)
22
- @connection = connection
23
- end
24
-
25
- def create(params)
26
- certificate = Certificate.new(@connection)
27
- certificate.create(params)
28
- end
29
-
30
- def add(certificate_data, certificate_password, certificate_format, dns_name)
31
- certificate = Certificate.new(@connection)
32
- certificate.add_certificate certificate_data, certificate_password, certificate_format, dns_name
33
- end
34
-
35
- def create_ssl_certificate(azure_dns_name)
36
- cert_params = { output_file: "winrm", key_length: 2048, cert_validity: 24,
37
- azure_dns_name: azure_dns_name }
38
- certificate = Certificate.new(@connection)
39
- thumbprint = certificate.create_ssl_certificate(cert_params)
40
- end
41
-
42
- def get_certificate(dns_name, fingerprint)
43
- certificate = Certificate.new(@connection)
44
- certificate.get_certificate(dns_name, fingerprint)
45
- end
46
- end
47
- end
48
-
49
- module Azure
50
- class Certificate
51
- attr_accessor :connection
52
- attr_accessor :cert_data, :fingerprint, :certificate_version
53
- def initialize(connection)
54
- @connection = connection
55
- @certificate_version = 2 # cf. RFC 5280 - to make it a "v3" certificate
56
- end
57
-
58
- def create(params)
59
- # If RSA private key has been specified, then generate an x 509 certificate from the
60
- # public part of the key
61
- @cert_data = generate_public_key_certificate_data({ ssh_key: params[:ssh_identity_file],
62
- ssh_key_passphrase: params[:identity_file_passphrase] })
63
- add_certificate @cert_data, "knifeazure", "pfx", params[:azure_dns_name]
64
-
65
- # Return the fingerprint to be used while adding role
66
- @fingerprint
67
- end
68
-
69
- def generate_public_key_certificate_data(params)
70
- # Generate OpenSSL RSA key from the mentioned ssh key path (and passphrase)
71
- key = OpenSSL::PKey::RSA.new(File.read(params[:ssh_key]), params[:ssh_key_passphrase])
72
- # Generate X 509 certificate
73
- ca = OpenSSL::X509::Certificate.new
74
- ca.version = @certificate_version
75
- ca.serial = Random.rand(65534) + 1 # 2 digit byte range random number for better security aspect
76
- ca.subject = OpenSSL::X509::Name.parse "/DC=org/DC=knife-plugin/CN=Opscode CA"
77
- ca.issuer = ca.subject # root CA's are "self-signed"
78
- ca.public_key = key.public_key # Assign the ssh-key's public part to the certificate
79
- ca.not_before = Time.now
80
- ca.not_after = ca.not_before + 2 * 365 * 24 * 60 * 60 # 2 years validity
81
- ef = OpenSSL::X509::ExtensionFactory.new
82
- ef.subject_certificate = ca
83
- ef.issuer_certificate = ca
84
- ca.add_extension(ef.create_extension("basicConstraints", "CA:TRUE", true))
85
- ca.add_extension(ef.create_extension("keyUsage", "keyCertSign, cRLSign", true))
86
- ca.add_extension(ef.create_extension("subjectKeyIdentifier", "hash", false))
87
- ca.add_extension(ef.create_extension("authorityKeyIdentifier", "keyid:always", false))
88
- ca.sign(key, OpenSSL::Digest::SHA256.new)
89
- # Generate the SHA1 fingerprint of the der format of the X 509 certificate
90
- @fingerprint = OpenSSL::Digest::SHA1.new(ca.to_der)
91
- # Create the pfx format of the certificate
92
- pfx = OpenSSL::PKCS12.create("knifeazure", "knife-azure-pfx", key, ca)
93
- # Encode the pfx format - upload this certificate
94
- Base64.strict_encode64(pfx.to_der)
95
- end
96
-
97
- def add_certificate(certificate_data, certificate_password, certificate_format, dns_name)
98
- # Generate XML to call the API
99
- # Add certificate to the hosted service
100
- builder = Nokogiri::XML::Builder.new do |xml|
101
- xml.CertificateFile("xmlns" => "http://schemas.microsoft.com/windowsazure") do
102
- xml.Data certificate_data
103
- xml.CertificateFormat certificate_format
104
- xml.Password certificate_password
105
- end
106
- end
107
- # Windows Azure API call
108
- @connection.query_azure("hostedservices/#{dns_name}/certificates", "post", builder.to_xml)
109
-
110
- # Check if certificate is available else raise error
111
- for attempt in 0..4
112
- Chef::Log.info "Waiting to get certificate ..."
113
- res = get_certificate(dns_name, @fingerprint)
114
- break unless res.empty?
115
- if attempt == 4
116
- raise "The certificate with thumbprint #{fingerprint} was not found."
117
- else
118
- sleep 5
119
- end
120
- end
121
- end
122
-
123
- def get_certificate(dns_name, fingerprint)
124
- @connection.query_azure("hostedservices/#{dns_name}/certificates/sha1-#{fingerprint}", "get").search("Certificate")
125
- end
126
-
127
- ######## SSL certificate generation for knife-azure ssl bootstrap ######
128
- def create_ssl_certificate(cert_params)
129
- file_path = cert_params[:output_file].sub(/\.(\w+)$/, "")
130
- path = prompt_for_file_path
131
- file_path = File.join(path, file_path) unless path.empty?
132
- cert_params[:domain] = prompt_for_domain
133
-
134
- rsa_key = generate_keypair cert_params[:key_length]
135
- cert = generate_certificate(rsa_key, cert_params)
136
- write_certificate_to_file cert, file_path, rsa_key, cert_params
137
- puts "*" * 70
138
- puts "Generated Certificates:"
139
- puts "- #{file_path}.pfx - PKCS12 format keypair. Contains both the public and private keys, usually used on the server."
140
- puts "- #{file_path}.b64 - Base64 encoded PKCS12 keypair. Contains both the public and private keys, for upload to the Azure REST API."
141
- puts "- #{file_path}.pem - Base64 encoded public certificate only. Required by the client to connect to the server."
142
- puts "Certificate Thumbprint: #{@thumbprint.to_s.upcase}"
143
- puts "*" * 70
144
-
145
- config[:ca_trust_file] = file_path + ".pem" if config[:ca_trust_file].nil?
146
- cert_data = File.read (file_path + ".b64")
147
- add_certificate cert_data, @winrm_cert_passphrase, "pfx", cert_params[:azure_dns_name]
148
- @thumbprint
149
- end
150
-
151
- def generate_keypair(key_length)
152
- OpenSSL::PKey::RSA.new(key_length.to_i)
153
- end
154
-
155
- def prompt_for_passphrase
156
- passphrase = ""
157
- begin
158
- print "Passphrases do not match. Try again.\n" unless passphrase.empty?
159
- print "Enter certificate passphrase (empty for no passphrase):"
160
- passphrase = STDIN.gets
161
- return passphrase.strip if passphrase == "\n"
162
-
163
- print "Enter same passphrase again:"
164
- confirm_passphrase = STDIN.gets
165
- end until passphrase == confirm_passphrase
166
- passphrase.strip
167
- end
168
-
169
- def prompt_for_file_path
170
- file_path = ""
171
- counter = 0
172
- begin
173
- print "Invalid location! \n" unless file_path.empty?
174
- print 'Enter the file path for certificates e.g. C:\Windows (empty for current location):'
175
- file_path = STDIN.gets
176
- stripped_file_path = file_path.strip
177
- return stripped_file_path if file_path == "\n"
178
-
179
- counter += 1
180
- exit(1) if counter == 3
181
- end until File.directory?(stripped_file_path)
182
- stripped_file_path
183
- end
184
-
185
- def prompt_for_domain
186
- counter = 0
187
- begin
188
- print "Enter the domain (mandatory):"
189
- domain = STDIN.gets
190
- domain = domain.strip
191
- counter += 1
192
- exit(1) if counter == 3
193
- end until !domain.empty?
194
- domain
195
- end
196
-
197
- def generate_certificate(rsa_key, cert_params)
198
- @hostname = "*"
199
- if cert_params[:domain]
200
- @hostname = "*." + cert_params[:domain]
201
- end
202
-
203
- # Create a self-signed X509 certificate from the rsa_key (unencrypted)
204
- cert = OpenSSL::X509::Certificate.new
205
- cert.version = 2
206
- cert.serial = Random.rand(65534) + 1 # 2 digit byte range random number for better security aspect
207
-
208
- cert.subject = OpenSSL::X509::Name.parse "/CN=#{@hostname}"
209
- cert.issuer = cert.subject
210
- cert.public_key = rsa_key.public_key
211
- cert.not_before = Time.now
212
- cert.not_after = cert.not_before + 2 * 365 * cert_params[:cert_validity].to_i * 60 * 60 # 2 years validity
213
- ef = OpenSSL::X509::ExtensionFactory.new
214
- ef.subject_certificate = cert
215
- ef.issuer_certificate = cert
216
- cert.add_extension(ef.create_extension("subjectKeyIdentifier", "hash", false))
217
- cert.add_extension(ef.create_extension("authorityKeyIdentifier", "keyid:always", false))
218
- cert.add_extension(ef.create_extension("extendedKeyUsage", "1.3.6.1.5.5.7.3.1", false))
219
- cert.sign(rsa_key, OpenSSL::Digest::SHA1.new)
220
- @thumbprint = OpenSSL::Digest::SHA1.new(cert.to_der)
221
- cert
222
- end
223
-
224
- def write_certificate_to_file(cert, file_path, rsa_key, cert_params)
225
- File.open(file_path + ".pem", "wb") { |f| f.print cert.to_pem }
226
- @winrm_cert_passphrase = prompt_for_passphrase unless @winrm_cert_passphrase
227
- pfx = OpenSSL::PKCS12.create("#{cert_params[:winrm_cert_passphrase]}", "winrmcert", rsa_key, cert)
228
- File.open(file_path + ".pfx", "wb") { |f| f.print pfx.to_der }
229
- File.open(file_path + ".b64", "wb") { |f| f.print Base64.strict_encode64(pfx.to_der) }
230
- end
231
-
232
- ########## SSL certificate generation ends ###########
233
-
234
- end
235
- end