chef-vault 1.2.5 → 2.0.1.pre

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -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