chef-vault 1.2.5 → 2.0.1.pre
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 +15 -0
- data/Changelog.md +17 -0
- data/KNIFE_EXAMPLES.md +169 -0
- data/README.md +83 -106
- data/bin/chef-vault +25 -19
- data/lib/chef-vault.rb +17 -9
- data/lib/chef-vault/certificate.rb +7 -28
- data/lib/chef-vault/chef_patch/api_client.rb +40 -0
- data/lib/chef-vault/chef_patch/user.rb +33 -0
- data/lib/chef-vault/exceptions.rb +27 -0
- data/lib/chef-vault/item.rb +243 -0
- data/lib/chef-vault/item_keys.rb +121 -0
- data/lib/chef-vault/user.rb +7 -28
- data/lib/chef-vault/version.rb +1 -1
- data/lib/chef/knife/Decrypt.rb +64 -0
- data/lib/chef/knife/encrypt_create.rb +91 -0
- data/lib/chef/knife/encrypt_delete.rb +62 -0
- data/lib/chef/knife/encrypt_remove.rb +100 -0
- data/lib/chef/knife/encrypt_rotate_keys.rb +62 -0
- data/lib/chef/knife/encrypt_update.rb +90 -0
- data/lib/{chef-vault/chef/offline.rb → chef/knife/mixin/compat.rb} +15 -11
- data/lib/chef/knife/mixin/helper.rb +50 -0
- data/spec/chef-vault_spec.rb +19 -30
- data/spec/item_keys_spec.rb +29 -0
- data/spec/item_spec.rb +33 -0
- metadata +23 -21
- data/lib/chef/knife/DecryptCert.rb +0 -59
- data/lib/chef/knife/DecryptPassword.rb +0 -58
- data/lib/chef/knife/EncryptCert.rb +0 -185
- data/lib/chef/knife/EncryptPassword.rb +0 -182
- data/lib/chef/knife/compat.rb +0 -71
@@ -1,185 +0,0 @@
|
|
1
|
-
# Description: Chef-Vault EncryptCert class
|
2
|
-
# Copyright 2013, Nordstrom, Inc.
|
3
|
-
|
4
|
-
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
-
# you may not use this file except in compliance with the License.
|
6
|
-
# You may obtain a copy of the License at
|
7
|
-
|
8
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
-
|
10
|
-
# Unless required by applicable law or agreed to in writing, software
|
11
|
-
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
-
# See the License for the specific language governing permissions and
|
14
|
-
# limitations under the License.
|
15
|
-
|
16
|
-
require 'chef/knife'
|
17
|
-
|
18
|
-
class EncryptCert < Chef::Knife
|
19
|
-
deps do
|
20
|
-
require 'chef/search/query'
|
21
|
-
require File.expand_path('../compat', __FILE__)
|
22
|
-
include ChefVault::Compat
|
23
|
-
end
|
24
|
-
|
25
|
-
banner "knife encrypt cert --search SEARCH --cert CERT --password PASSWORD --name NAME --admins ADMINS"
|
26
|
-
|
27
|
-
option :search,
|
28
|
-
:short => '-S SEARCH',
|
29
|
-
:long => '--search SEARCH',
|
30
|
-
:description => 'node search for nodes to encrypt to'
|
31
|
-
|
32
|
-
option :cert,
|
33
|
-
:short => '-C CERT',
|
34
|
-
:long => '--cert CERT',
|
35
|
-
:description => 'cert with contents to encrypt'
|
36
|
-
|
37
|
-
option :admins,
|
38
|
-
:short => '-A ADMINS',
|
39
|
-
:long => '--admins ADMINS',
|
40
|
-
:description => 'administrators who can decrypt certificate'
|
41
|
-
|
42
|
-
option :password,
|
43
|
-
:short => '-P PASSWORD',
|
44
|
-
:long => '--password PASSWORD',
|
45
|
-
:description => 'optional pfx password'
|
46
|
-
|
47
|
-
option :name,
|
48
|
-
:short => '-N NAME',
|
49
|
-
:long => '--name NAME',
|
50
|
-
:description => 'optional data bag name'
|
51
|
-
|
52
|
-
def run
|
53
|
-
unless config[:search]
|
54
|
-
puts("You must supply either -S or --search")
|
55
|
-
exit 1
|
56
|
-
end
|
57
|
-
unless config[:cert]
|
58
|
-
puts("You must supply either -C or --cert")
|
59
|
-
exit 1
|
60
|
-
end
|
61
|
-
unless config[:admins]
|
62
|
-
puts("You must supply either -A or --admins")
|
63
|
-
exit 1
|
64
|
-
end
|
65
|
-
extend_context_object(self)
|
66
|
-
|
67
|
-
data_bag = "certs"
|
68
|
-
data_bag_path = "./data_bags/#{data_bag}"
|
69
|
-
|
70
|
-
unless ::File.exists?(data_bag_path)
|
71
|
-
require 'fileutils'
|
72
|
-
|
73
|
-
puts("INFO: #{data_bag_path} does not exist, creating...")
|
74
|
-
FileUtils.mkdir_p(data_bag_path)
|
75
|
-
end
|
76
|
-
|
77
|
-
node_search = config[:search]
|
78
|
-
admins = config[:admins]
|
79
|
-
file_to_encrypt = config[:cert]
|
80
|
-
contents = open(file_to_encrypt, "rb").read
|
81
|
-
name = config[:name] ? config[:name].gsub(".", "_") : File.basename(file_to_encrypt, ".*").gsub(".", "_")
|
82
|
-
|
83
|
-
current_dbi = Hash.new
|
84
|
-
current_dbi_keys = Hash.new
|
85
|
-
if File.exists?("#{data_bag_path}/#{name}_keys.json") && File.exists?("#{data_bag_path}/#{name}.json")
|
86
|
-
current_dbi_keys = JSON.parse(File.open("#{data_bag_path}/#{name}_keys.json"){ |file| file.read() })
|
87
|
-
current_dbi = JSON.parse(File.open("#{data_bag_path}/#{name}_keys.json"){ |file| file.read() })
|
88
|
-
|
89
|
-
unless equal?(data_bag, name, "contents", contents)
|
90
|
-
puts("FATAL: Content in #{data_bag_path}/#{name}.json does not match content in file supplied!")
|
91
|
-
exit 1
|
92
|
-
end
|
93
|
-
else
|
94
|
-
puts("INFO: Existing data bag #{data_bag}/#{name} does not exist in #{data_bag_path}, continuing as fresh build...")
|
95
|
-
end
|
96
|
-
|
97
|
-
# Get the public keys for all of the nodes to encrypt for. Skipping the nodes that are already in
|
98
|
-
# the data bag
|
99
|
-
keyfob = Hash.new
|
100
|
-
public_keys = search(:node, node_search).map(&:name).map do |client|
|
101
|
-
begin
|
102
|
-
if current_dbi_keys[client]
|
103
|
-
puts("INFO: Skipping #{client} as it is already in the data bag...")
|
104
|
-
else
|
105
|
-
puts("INFO: Adding #{client} to public_key array...")
|
106
|
-
keyfob[client] = get_client_public_key(client)
|
107
|
-
end
|
108
|
-
rescue Exception => node_error
|
109
|
-
puts("WARNING: Caught exception: #{node_error.message} while processing #{client}, so skipping...")
|
110
|
-
end
|
111
|
-
end
|
112
|
-
|
113
|
-
# Get the public keys for the admin users, skipping users already in the data bag
|
114
|
-
public_keys << admins.split(/[\s,]+/).map do |user|
|
115
|
-
begin
|
116
|
-
if current_dbi_keys[user]
|
117
|
-
puts("INFO: Skipping #{user} as it is already in the data bag")
|
118
|
-
else
|
119
|
-
puts("INFO: Adding #{user} to public_key array...")
|
120
|
-
keyfob[user] = get_user_public_key(user)
|
121
|
-
end
|
122
|
-
rescue Exception => user_error
|
123
|
-
puts("WARNING: Caught exception: #{user_error.message} while processing #{user}, so skipping...")
|
124
|
-
end
|
125
|
-
end
|
126
|
-
|
127
|
-
if public_keys.length == 0
|
128
|
-
puts "A node search for #{node_search} returned no results"
|
129
|
-
exit 1
|
130
|
-
end
|
131
|
-
|
132
|
-
# Get the current secret, is nil if current secret does not exist yet
|
133
|
-
current_secret = get_shared_secret(data_bag, name)
|
134
|
-
data_bag_shared_key = current_secret ? current_secret : OpenSSL::PKey::RSA.new(245).to_pem.lines.to_a[1..-2].join
|
135
|
-
enc_db_key_dbi = current_dbi_keys.empty? ? Mash.new({id: "#{name}_keys"}) : current_dbi_keys
|
136
|
-
|
137
|
-
# Encrypt for every new node not already in the data bag
|
138
|
-
keyfob.each do |node,pkey|
|
139
|
-
puts("INFO: Encrypting for #{node}...")
|
140
|
-
enc_db_key_dbi[node] = Base64.encode64(pkey.public_encrypt(data_bag_shared_key))
|
141
|
-
end unless keyfob.empty?
|
142
|
-
|
143
|
-
# Delete existing keys data bag and rewrite the whole bag from memory
|
144
|
-
puts("INFO: Writing #{data_bag_path}/#{name}_keys.json...")
|
145
|
-
File.delete("#{data_bag_path}/#{name}_keys.json") if File.exists?("#{data_bag_path}/#{name}_keys.json")
|
146
|
-
File.open("#{data_bag_path}/#{name}_keys.json",'w'){ |file| file.write(JSON.pretty_generate(enc_db_key_dbi)) }
|
147
|
-
|
148
|
-
# If the existing certificate bag does not exist, write it out with the correct certificate
|
149
|
-
# Otherwise leave the existing bag alone
|
150
|
-
if current_dbi.empty?
|
151
|
-
dbi_mash = Mash.new({id: name, contents: contents})
|
152
|
-
dbi_mash.merge!({password: config[:password]}) if config[:password]
|
153
|
-
dbi = Chef::DataBagItem.from_hash(dbi_mash)
|
154
|
-
edbi = Chef::EncryptedDataBagItem.encrypt_data_bag_item(dbi, data_bag_shared_key)
|
155
|
-
|
156
|
-
puts("INFO: Writing #{data_bag_path}/#{name}.json...")
|
157
|
-
File.open("#{data_bag_path}/#{name}.json",'w'){ |file| file.write(JSON.pretty_generate(edbi)) }
|
158
|
-
end
|
159
|
-
|
160
|
-
puts("INFO: Successfully wrote #{data_bag_path}/#{name}.json & #{data_bag_path}/#{name}_keys.json!")
|
161
|
-
end
|
162
|
-
|
163
|
-
def equal?(db, dbi, key, value)
|
164
|
-
data_bag_path = "./data_bags/#{db}"
|
165
|
-
|
166
|
-
shared_secret = get_shared_secret(db, dbi)
|
167
|
-
dbi = JSON.parse(File.open("#{data_bag_path}/#{dbi}.json") { |file| file.read() })
|
168
|
-
dbi = Chef::EncryptedDataBagItem.new dbi, shared_secret
|
169
|
-
|
170
|
-
dbi[key] == value
|
171
|
-
end
|
172
|
-
|
173
|
-
def get_shared_secret(db, dbi)
|
174
|
-
data_bag_path = "./data_bags/#{db}"
|
175
|
-
|
176
|
-
private_key = OpenSSL::PKey::RSA.new(open(Chef::Config[:client_key]).read())
|
177
|
-
key = File.exists?("#{data_bag_path}/#{dbi}_keys.json") ? JSON.parse(File.open("#{data_bag_path}/#{dbi}_keys.json"){ |file| file.read() }) : nil
|
178
|
-
|
179
|
-
begin
|
180
|
-
private_key.private_decrypt(Base64.decode64(key[Chef::Config[:node_name]]))
|
181
|
-
rescue
|
182
|
-
nil
|
183
|
-
end
|
184
|
-
end
|
185
|
-
end
|
@@ -1,182 +0,0 @@
|
|
1
|
-
# Description: Chef-Vault EncryptPassword class
|
2
|
-
# Copyright 2013, Nordstrom, Inc.
|
3
|
-
|
4
|
-
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
-
# you may not use this file except in compliance with the License.
|
6
|
-
# You may obtain a copy of the License at
|
7
|
-
|
8
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
-
|
10
|
-
# Unless required by applicable law or agreed to in writing, software
|
11
|
-
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
-
# See the License for the specific language governing permissions and
|
14
|
-
# limitations under the License.
|
15
|
-
|
16
|
-
require 'chef/knife'
|
17
|
-
|
18
|
-
class EncryptPassword < Chef::Knife
|
19
|
-
deps do
|
20
|
-
require 'chef/search/query'
|
21
|
-
require File.expand_path('../compat', __FILE__)
|
22
|
-
include ChefVault::Compat
|
23
|
-
end
|
24
|
-
|
25
|
-
banner "knife encrypt password --search SEARCH --username USERNAME --password PASSWORD --admins ADMINS"
|
26
|
-
|
27
|
-
option :search,
|
28
|
-
:short => '-S SEARCH',
|
29
|
-
:long => '--search SEARCH',
|
30
|
-
:description => 'node search for nodes to encrypt for'
|
31
|
-
|
32
|
-
option :username,
|
33
|
-
:short => '-U USERNAME',
|
34
|
-
:long => '--username USERNAME',
|
35
|
-
:description => 'username of account to encrypt'
|
36
|
-
|
37
|
-
option :password,
|
38
|
-
:short => '-P PASSWORD',
|
39
|
-
:long => '--password PASSWORD',
|
40
|
-
:description => 'password of account to encrypt'
|
41
|
-
|
42
|
-
option :admins,
|
43
|
-
:short => '-A ADMINS',
|
44
|
-
:long => '--admins ADMINS',
|
45
|
-
:description => 'administrators who can decrypt password'
|
46
|
-
|
47
|
-
def run
|
48
|
-
unless config[:search]
|
49
|
-
puts("You must supply either -S or --search")
|
50
|
-
exit 1
|
51
|
-
end
|
52
|
-
unless config[:username]
|
53
|
-
puts("You must supply either -U or --username")
|
54
|
-
exit 1
|
55
|
-
end
|
56
|
-
unless config[:password]
|
57
|
-
puts("You must supply either -P or --password")
|
58
|
-
exit 1
|
59
|
-
end
|
60
|
-
unless config[:admins]
|
61
|
-
puts("You must supply either -A or --admins")
|
62
|
-
exit 1
|
63
|
-
end
|
64
|
-
|
65
|
-
extend_context_object(self)
|
66
|
-
|
67
|
-
data_bag = "passwords"
|
68
|
-
data_bag_path = "./data_bags/#{data_bag}"
|
69
|
-
|
70
|
-
unless ::File.exists?(data_bag_path)
|
71
|
-
require 'fileutils'
|
72
|
-
|
73
|
-
puts("INFO: #{data_bag_path} does not exist, creating...")
|
74
|
-
FileUtils.mkdir_p(data_bag_path)
|
75
|
-
end
|
76
|
-
|
77
|
-
node_search = config[:search]
|
78
|
-
admins = config[:admins]
|
79
|
-
username = config[:username]
|
80
|
-
password = config[:password]
|
81
|
-
current_dbi = Hash.new
|
82
|
-
current_dbi_keys = Hash.new
|
83
|
-
if File.exists?("#{data_bag_path}/#{username}_keys.json") && File.exists?("#{data_bag_path}/#{username}.json")
|
84
|
-
current_dbi_keys = JSON.parse(File.open("#{data_bag_path}/#{username}_keys.json"){ |file| file.read() })
|
85
|
-
current_dbi = JSON.parse(File.open("#{data_bag_path}/#{username}.json"){ |file| file.read() })
|
86
|
-
|
87
|
-
unless equal?(data_bag, username, "password", password)
|
88
|
-
puts("FATAL: Password in #{data_bag_path}/#{username}.json does not match password supplied!")
|
89
|
-
exit 1
|
90
|
-
end
|
91
|
-
else
|
92
|
-
puts("INFO: Existing data bag #{data_bag}/#{username} does not exist in #{data_bag_path}, continuing as fresh build...")
|
93
|
-
end
|
94
|
-
|
95
|
-
# Get the public keys for all of the nodes to encrypt for. Skipping the nodes that are already in
|
96
|
-
# the data bag
|
97
|
-
keyfob = Hash.new
|
98
|
-
public_keys = search(:node, node_search).map(&:name).map do |client|
|
99
|
-
begin
|
100
|
-
if current_dbi_keys[client]
|
101
|
-
puts("INFO: Skipping #{client} as it is already in the data bag...")
|
102
|
-
else
|
103
|
-
puts("INFO: Adding #{client} to public_key array...")
|
104
|
-
keyfob[client] = get_client_public_key(client)
|
105
|
-
end
|
106
|
-
rescue Exception => node_error
|
107
|
-
puts("WARNING: Caught exception: #{node_error.message} while processing #{client}, so skipping...")
|
108
|
-
end
|
109
|
-
end
|
110
|
-
|
111
|
-
# Get the public keys for the admin users, skipping users already in the data bag
|
112
|
-
public_keys << admins.split(/[\s,]+/).map do |user|
|
113
|
-
begin
|
114
|
-
if current_dbi_keys[user]
|
115
|
-
puts("INFO: Skipping #{user} as it is already in the data bag")
|
116
|
-
else
|
117
|
-
puts("INFO: Adding #{user} to public_key array...")
|
118
|
-
keyfob[user] = get_user_public_key(user)
|
119
|
-
end
|
120
|
-
rescue Exception => user_error
|
121
|
-
puts("WARNING: Caught exception: #{user_error.message} while processing #{user}, so skipping...")
|
122
|
-
end
|
123
|
-
end
|
124
|
-
|
125
|
-
if public_keys.length == 0
|
126
|
-
puts "A node search for #{node_search} returned no results"
|
127
|
-
exit 1
|
128
|
-
end
|
129
|
-
|
130
|
-
# Get the current secret, is nil if current secret does not exist yet
|
131
|
-
current_secret = get_shared_secret(data_bag, username)
|
132
|
-
data_bag_shared_key = current_secret ? current_secret : OpenSSL::PKey::RSA.new(245).to_pem.lines.to_a[1..-2].join
|
133
|
-
enc_db_key_dbi = current_dbi_keys.empty? ? Mash.new({id: "#{username}_keys"}) : current_dbi_keys
|
134
|
-
|
135
|
-
# Encrypt for every new node not already in the data bag
|
136
|
-
keyfob.each do |node,pkey|
|
137
|
-
puts("INFO: Encrypting for #{node}...")
|
138
|
-
enc_db_key_dbi[node] = Base64.encode64(pkey.public_encrypt(data_bag_shared_key))
|
139
|
-
end unless keyfob.empty?
|
140
|
-
|
141
|
-
# Delete existing keys data bag and rewrite the whole bag from memory
|
142
|
-
puts("INFO: Writing #{data_bag_path}/#{username}_keys.json...")
|
143
|
-
File.delete("#{data_bag_path}/#{username}_keys.json") if File.exists?("#{data_bag_path}/#{username}_keys.json")
|
144
|
-
File.open("#{data_bag_path}/#{username}_keys.json",'w'){ |file| file.write(JSON.pretty_generate(enc_db_key_dbi)) }
|
145
|
-
|
146
|
-
# If the existing password bag does not exist, write it out with the correct password
|
147
|
-
# Otherwise leave the existing bag alone
|
148
|
-
if current_dbi.empty?
|
149
|
-
dbi_mash = Mash.new({id: username, username: username, password: password})
|
150
|
-
dbi = Chef::DataBagItem.from_hash(dbi_mash)
|
151
|
-
edbi = Chef::EncryptedDataBagItem.encrypt_data_bag_item(dbi, data_bag_shared_key)
|
152
|
-
|
153
|
-
puts("INFO: Writing #{data_bag_path}/#{username}.json...")
|
154
|
-
File.open("#{data_bag_path}/#{username}.json",'w'){ |file| file.write(JSON.pretty_generate(edbi)) }
|
155
|
-
end
|
156
|
-
|
157
|
-
puts("INFO: Successfully wrote #{data_bag_path}/#{username}.json & #{data_bag_path}/#{username}_keys.json!")
|
158
|
-
end
|
159
|
-
|
160
|
-
def equal?(db, dbi, key, value)
|
161
|
-
data_bag_path = "./data_bags/#{db}"
|
162
|
-
|
163
|
-
shared_secret = get_shared_secret(db, dbi)
|
164
|
-
dbi = JSON.parse(open("#{data_bag_path}/#{dbi}.json").read())
|
165
|
-
dbi = Chef::EncryptedDataBagItem.new dbi, shared_secret
|
166
|
-
|
167
|
-
dbi[key] == value
|
168
|
-
end
|
169
|
-
|
170
|
-
def get_shared_secret(db, dbi)
|
171
|
-
data_bag_path = "./data_bags/#{db}"
|
172
|
-
|
173
|
-
private_key = OpenSSL::PKey::RSA.new(open(Chef::Config[:client_key]).read())
|
174
|
-
key = File.exists?("#{data_bag_path}/#{dbi}_keys.json") ? JSON.parse(File.open("#{data_bag_path}/#{dbi}_keys.json"){ |file| file.read() }) : nil
|
175
|
-
|
176
|
-
begin
|
177
|
-
private_key.private_decrypt(Base64.decode64(key[Chef::Config[:node_name]]))
|
178
|
-
rescue
|
179
|
-
nil
|
180
|
-
end
|
181
|
-
end
|
182
|
-
end
|
data/lib/chef/knife/compat.rb
DELETED
@@ -1,71 +0,0 @@
|
|
1
|
-
# Description: ChefVault::Compat module
|
2
|
-
# Copyright 2013, Nordstrom, Inc.
|
3
|
-
|
4
|
-
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
-
# you may not use this file except in compliance with the License.
|
6
|
-
# You may obtain a copy of the License at
|
7
|
-
|
8
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
-
|
10
|
-
# Unless required by applicable law or agreed to in writing, software
|
11
|
-
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
-
# See the License for the specific language governing permissions and
|
14
|
-
# limitations under the License.
|
15
|
-
|
16
|
-
# Make a wraper to chef10/11 "shef/shell" changes
|
17
|
-
|
18
|
-
class ChefVault
|
19
|
-
module Compat
|
20
|
-
require 'chef/version'
|
21
|
-
def extend_context_object(obj)
|
22
|
-
if Chef::VERSION.to_i >= 11
|
23
|
-
require "chef/shell/ext"
|
24
|
-
Shell::Extensions.extend_context_object(obj)
|
25
|
-
else
|
26
|
-
require 'chef/shef/ext'
|
27
|
-
Shef::Extensions.extend_context_object(obj)
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
def get_client_public_key(client)
|
32
|
-
get_public_key(api.get("clients/#{client}"))
|
33
|
-
end
|
34
|
-
|
35
|
-
def get_user_public_key(user)
|
36
|
-
begin
|
37
|
-
user = api.get("users/#{user}")
|
38
|
-
rescue Exception
|
39
|
-
puts("INFO: Could not locate user #{user}, searching for client key instead")
|
40
|
-
user = api.get("clients/#{user}")
|
41
|
-
end
|
42
|
-
get_public_key(user)
|
43
|
-
end
|
44
|
-
|
45
|
-
def get_public_key(client)
|
46
|
-
# Check the response back from the api call to see if
|
47
|
-
# we get 'certificate' which is Chef 10 or just
|
48
|
-
# 'public_key' which is Chef 11
|
49
|
-
unless client.is_a?(Chef::ApiClient)
|
50
|
-
name = client['name']
|
51
|
-
certificate = client['certificate']
|
52
|
-
public_key = client['public_key']
|
53
|
-
|
54
|
-
client = Chef::ApiClient.new
|
55
|
-
client.name name
|
56
|
-
client.admin false
|
57
|
-
|
58
|
-
if certificate
|
59
|
-
cert_der = OpenSSL::X509::Certificate.new certificate
|
60
|
-
client.public_key cert_der.public_key.to_s
|
61
|
-
else
|
62
|
-
client.public_key public_key
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
public_key = OpenSSL::PKey::RSA.new client.public_key
|
67
|
-
|
68
|
-
public_key
|
69
|
-
end
|
70
|
-
end
|
71
|
-
end
|