veil 0.3.0 → 0.3.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 6101318787572fead55a5c0c06c46227f840114d
4
- data.tar.gz: af7f78cc9004e614ba49757eda9cba8996646f8d
2
+ SHA256:
3
+ metadata.gz: 3af8aaa1c4ac8737e148057359becae4bc29fcd2e4ba93a5e783f3ea840097e0
4
+ data.tar.gz: e9de20099a2d0a0288bdbd75f831471d83473ea284dd662b2fc9393bce370432
5
5
  SHA512:
6
- metadata.gz: 5f50e2f7e67dce633ec09ca5918e37949ed0e073499ac0878050ad40ed598f5a455ed01e635d8fb0bb13238e785b5204ec1ae4d9770bb73bd5067baf8c19c68c
7
- data.tar.gz: 241a21af2f050f4c193b6389a9f69673b1f913f24755cb54f7750ad83366cf363dc301278057faf9ed181489ffaa4c698a65684e197f8a1bcd9964470361c9a5
6
+ metadata.gz: 4c9f9d837b56c7c1bd19283a8442761ef66ce108a09427ac9a42162c21a44465f26fddeb50480c2c44c9dd7d15890d77574116af854f99ea001d870615e2b013
7
+ data.tar.gz: cce7ba9c026d5ccce2cdaff51974919a7d8d85d8e97fa97e8f6f8a28c2ce2097798250728e1f94b71506711ce5aeb54cafdcdaf5ceefbd064d28bdcb3e7fc806
@@ -1,10 +1,16 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  require 'veil'
4
+ require 'json'
4
5
  require 'optparse'
5
6
 
6
7
  options = {
7
- secrets_file: "/etc/opscode/private-chef-secrets.json"
8
+ secrets_file: "/etc/opscode/private-chef-secrets.json",
9
+ pack: false,
10
+ use_file: false,
11
+ debug: false,
12
+ secrets: [],
13
+ optional_secrets: []
8
14
  }
9
15
 
10
16
  OptionParser.new do |opts|
@@ -14,8 +20,20 @@ OptionParser.new do |opts|
14
20
  options[:debug] = d
15
21
  end
16
22
 
17
- opts.on("-s SECRETS_SPEC", "--secrets SECRETS_SPEC", "A comma seperated list of secrets to put in the environment") do |spec|
18
- options[:secrets] = spec.split(",")
23
+ opts.on("--pack", "Pass secrets in a single CHEF_SECRETS_DATA environment variable") do |p|
24
+ options[:pack] = p
25
+ end
26
+
27
+ opts.on("--use-file", "Pass secrets via a unlinked file available at the FD specified in CHEF_SECRETS_FD") do |fd|
28
+ options[:use_file] = fd
29
+ end
30
+
31
+ opts.on("-s SECRET_SPEC", "--secret SECRET_SPEC", "Secret to put in the environment") do |spec|
32
+ options[:secrets] << spec
33
+ end
34
+
35
+ opts.on("-o SECRET_SPEC", "--optional-secret SECRET_SPEC", "Optional secrets to put in the environment (if it exists)") do |spec|
36
+ options[:optional_secrets] << spec
19
37
  end
20
38
 
21
39
  opts.on("-f SECRETS_FILE", "--secrets-file SECRETS_FILE", "Location of veil-managed secrets file. Default: /etc/opscode/private-chef-secrets.json") do |file|
@@ -23,25 +41,64 @@ OptionParser.new do |opts|
23
41
  end
24
42
  end.parse!
25
43
 
26
- def env_name_from_secret_spec(secret)
44
+ def from_secret_spec(secret, start = {})
27
45
  parts = secret.split("=")
28
- case parts.length
29
- when 1
30
- ["CHEF_SECRET_#{parts[0].upcase}", parts[0]]
31
- when 2
32
- [parts[0].upcase, parts[1]]
46
+ env_name, secret_name = case parts.length
47
+ when 1
48
+ ["CHEF_SECRET_#{parts[0].upcase}", parts[0]]
49
+ when 2
50
+ [parts[0].upcase, parts[1]]
51
+ else
52
+ raise "Bad secret spec: #{secret}"
53
+ end
54
+
55
+ start.merge({ args: secret_name.split("."),
56
+ env_name: env_name,
57
+ name: secret_name })
58
+ end
59
+
60
+ veil = Veil::CredentialCollection::ChefSecretsFile.from_file(options[:secrets_file])
61
+ packed_data = Hash.new()
62
+
63
+ secrets = options[:secrets].map { |spec| from_secret_spec(spec) }
64
+ secrets += options[:optional_secrets].map { |spec| from_secret_spec(spec, { optional: true }) }
65
+
66
+ secrets.each do |secret|
67
+ veil_args = secret[:args]
68
+
69
+ begin
70
+ secret_value = veil.get(*veil_args)
71
+ rescue
72
+ raise unless secret[:optional]
73
+ next
74
+ end
75
+
76
+ if options[:pack] || options[:use_file]
77
+ STDERR.puts "Packing data using #{veil_args.inspect} and #{secret_value}" if options[:debug]
78
+ if veil_args.length == 2
79
+ packed_data[veil_args[0]] ||= {}
80
+ packed_data[veil_args[0]][veil_args[1]] = secret_value
81
+ elsif veil_args.length == 1
82
+ packed_data[veil_args[0]] = secret_value
83
+ elsif !secret[:optional]
84
+ raise "Invalid secrets name: #{secret[:name]}"
85
+ end
33
86
  else
34
- raise "Bad secret spec: #{secret}"
87
+ STDERR.puts "Setting #{secret[:env_name]}=#{secret_value}" if options[:debug]
88
+ ENV[secret[:env_name]] = secret_value
35
89
  end
36
90
  end
37
91
 
38
- veil = Veil::CredentialCollection::ChefSecretsFile.from_file(options[:secrets_file])
39
- Array(options[:secrets]).each do |secret|
40
- env_name, secret_name = env_name_from_secret_spec(secret)
41
- veil_args = secret_name.split(".")
42
- secret_value = veil.get(*veil_args)
43
- STDERR.puts "Setting #{env_name}=#{secret_value}" if options[:debug]
44
- ENV[env_name] = secret_value
92
+ if options[:pack] && !options[:use_file]
93
+ ENV['CHEF_SECRETS_DATA'] = packed_data.to_json
94
+ end
95
+
96
+ if options[:use_file]
97
+ rd, wd = IO.pipe
98
+ wd.puts packed_data.to_json
99
+ wd.close
100
+ rd.close_on_exec = false
101
+ ENV['CHEF_SECRETS_FD'] = rd.to_i.to_s
45
102
  end
46
103
 
47
- exec(*ARGV)
104
+ exec(*ARGV, close_others: false)
@@ -1,5 +1,7 @@
1
1
  require "veil/credential_collection/base"
2
+ require "veil/credential_collection/chef_secrets_fd"
2
3
  require "veil/credential_collection/chef_secrets_file"
4
+ require "veil/credential_collection/chef_secrets_env"
3
5
 
4
6
  module Veil
5
7
  class CredentialCollection
@@ -8,6 +10,10 @@ module Veil
8
10
  klass = case opts[:provider]
9
11
  when 'chef-secrets-file'
10
12
  ChefSecretsFile
13
+ when 'chef-secrets-env'
14
+ ChefSecretsEnv
15
+ when 'chef-secrets-fd'
16
+ ChefSecretsFd
11
17
  else
12
18
  raise UnknownProvider, "Unknown provider: #{opts[:provider]}"
13
19
  end
@@ -176,6 +176,39 @@ module Veil
176
176
  end
177
177
  end
178
178
 
179
+ def credentials_as_hash
180
+ hash = Hash.new
181
+
182
+ credentials.each do |cred_or_group_name, cred_or_group_attrs|
183
+ if cred_or_group_attrs.is_a?(Hash)
184
+ cred_or_group_attrs.each do |name, cred|
185
+ hash[cred_or_group_name] ||= Hash.new
186
+ hash[cred_or_group_name][name] = cred.to_hash
187
+ end
188
+ else
189
+ hash[cred_or_group_name] = cred_or_group_attrs.to_hash
190
+ end
191
+ end
192
+
193
+ hash
194
+ end
195
+
196
+ def credentials_for_export
197
+ hash = Hash.new
198
+
199
+ credentials.each do |namespace, cred_or_creds|
200
+ if cred_or_creds.is_a?(Veil::Credential)
201
+ hash[namespace] = cred_or_creds.value
202
+ else
203
+ hash[namespace] = {}
204
+ cred_or_creds.each { |name, cred| hash[namespace][name] = cred.value }
205
+ end
206
+ end
207
+
208
+ hash
209
+ end
210
+ alias_method :legacy_credentials_hash, :credentials_for_export
211
+
179
212
  private
180
213
 
181
214
  def add_from_params(params)
@@ -222,21 +255,17 @@ module Veil
222
255
  expanded
223
256
  end
224
257
 
225
- def credentials_as_hash
226
- hash = Hash.new
227
-
228
- credentials.each do |cred_or_group_name, cred_or_group_attrs|
229
- if cred_or_group_attrs.is_a?(Hash)
230
- cred_or_group_attrs.each do |name, cred|
231
- hash[cred_or_group_name] ||= Hash.new
232
- hash[cred_or_group_name][name] = cred.to_hash
233
- end
234
- else
235
- hash[cred_or_group_name] = cred_or_group_attrs.to_hash
258
+ def import_credentials_hash(hash)
259
+ hash.each do |namespace, creds_hash|
260
+ credentials[namespace.to_s] ||= Hash.new
261
+ creds_hash.each do |cred, value|
262
+ credentials[namespace.to_s][cred.to_s] = Veil::Credential.new(
263
+ name: cred.to_s,
264
+ value: value,
265
+ length: value.length
266
+ )
236
267
  end
237
268
  end
238
-
239
- hash
240
269
  end
241
270
  end
242
271
  end
@@ -0,0 +1,43 @@
1
+ require "veil/credential_collection/base"
2
+ require "json"
3
+
4
+ module Veil
5
+ class CredentialCollection
6
+ class ChefSecretsEnv < Base
7
+
8
+ # Create a new ChefSecretsEnv
9
+ #
10
+ # @param [Hash] opts
11
+ # a hash of options to pass to the constructor
12
+ def initialize(opts = {})
13
+ var_name = opts[:var_name] || 'CHEF_SECRETS_DATA'
14
+
15
+ @credentials = {}
16
+ import_credentials_hash(inflate_secrets_from_environment(var_name))
17
+ end
18
+
19
+ # Unsupported methods
20
+ def rotate
21
+ raise NotImplementedError
22
+ end
23
+ alias_method :rotate_credentials, :rotate
24
+ alias_method :save, :rotate
25
+
26
+ def inflate_secrets_from_environment(var_name)
27
+ value = ENV[var_name]
28
+ unless value
29
+ msg = "Env var #{var_name} has not been set. This should by done by "\
30
+ "launching this application via veil-env-wrapper."
31
+ raise InvalidCredentialCollectionEnv.new(msg)
32
+ end
33
+
34
+ begin
35
+ JSON.parse(value)
36
+ rescue JSON::ParserError => e
37
+ msg = "Env var #{var_name} could not be parsed: #{e.message}"
38
+ raise InvalidCredentialCollectionEnv.new(msg)
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,57 @@
1
+ require "veil/exceptions"
2
+ require "veil/credential_collection/base"
3
+ require "json"
4
+
5
+ module Veil
6
+ class CredentialCollection
7
+ class ChefSecretsFd < Base
8
+
9
+ # Create a new ChefSecretsFd
10
+ #
11
+ # @param [Hash] opts
12
+ # ignored
13
+ def initialize(opts = {})
14
+ @credentials = {}
15
+ import_credentials_hash(inflate_secrets_from_fd)
16
+ end
17
+
18
+ # Unsupported methods
19
+ def rotate
20
+ raise NotImplementedError
21
+ end
22
+ alias_method :rotate_credentials, :rotate
23
+ alias_method :save, :rotate
24
+
25
+ def inflate_secrets_from_fd
26
+ if ENV['CHEF_SECRETS_FD'].nil?
27
+ raise InvalidCredentialCollectionFd.new("CHEF_SECRETS_FD not found in environment")
28
+ end
29
+
30
+ fd = ENV['CHEF_SECRETS_FD'].to_i
31
+ value = nil
32
+
33
+ begin
34
+ file = IO.new(fd, "r")
35
+ value = file.gets
36
+ rescue StandardError => e
37
+ msg = "A problem occured trying to read passed file descriptor: #{e}"
38
+ raise InvalidCredentialCollectionFd.new(msg)
39
+ ensure
40
+ file.close if file
41
+ end
42
+
43
+ if !value
44
+ msg = "File at CHEF_SECRETS_FD (#{fd}) did not contain any data!"
45
+ raise InvalidCredentialCollectionFd.new(msg)
46
+ end
47
+
48
+ begin
49
+ JSON.parse(value)
50
+ rescue JSON::ParserError => e
51
+ msg = "Chef secrets data could not be parsed: #{e.message}"
52
+ raise InvalidCredentialCollectionFd.new(msg)
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -48,7 +48,7 @@ module Veil
48
48
  opts[:version] = CURRENT_VERSION
49
49
  super(opts)
50
50
 
51
- import_legacy_credentials(hash) if import_existing && legacy
51
+ import_credentials_hash(hash) if import_existing && legacy
52
52
  end
53
53
 
54
54
  # Set the secrets file path
@@ -64,6 +64,12 @@ module Veil
64
64
  FileUtils.mkdir_p(File.dirname(path))
65
65
 
66
66
  f = Tempfile.new("veil") # defaults to mode 0600
67
+
68
+ if existing
69
+ @user ||= existing.uid
70
+ @group ||= existing.gid
71
+ end
72
+
67
73
  FileUtils.chown(user, group, f.path) if user
68
74
  f.puts(JSON.pretty_generate(secrets_hash))
69
75
  f.flush
@@ -78,33 +84,10 @@ module Veil
78
84
  { "veil" => to_h }
79
85
  end
80
86
 
81
- def credentials_for_export
82
- hash = Hash.new
83
-
84
- credentials.each do |namespace, cred_or_creds|
85
- if cred_or_creds.is_a?(Veil::Credential)
86
- hash[namespace] = cred_or_creds.value
87
- else
88
- hash[namespace] = {}
89
- cred_or_creds.each { |name, cred| hash[namespace][name] = cred.value }
90
- end
91
- end
92
-
93
- hash
94
- end
95
- alias_method :legacy_credentials_hash, :credentials_for_export
96
-
97
- def import_legacy_credentials(hash)
98
- hash.each do |namespace, creds_hash|
99
- credentials[namespace.to_s] ||= Hash.new
100
- creds_hash.each do |cred, value|
101
- credentials[namespace.to_s][cred.to_s] = Veil::Credential.new(
102
- name: cred.to_s,
103
- value: value,
104
- length: value.length
105
- )
106
- end
107
- end
87
+ def existing
88
+ @existing ||= File.stat(path)
89
+ rescue Errno::ENOENT
90
+ nil
108
91
  end
109
92
  end
110
93
  end
@@ -3,7 +3,10 @@ module Veil
3
3
  class InvalidSecret < StandardError; end
4
4
  class InvalidParameter < StandardError; end
5
5
  class InvalidHasher < StandardError; end
6
- class InvalidCredentialCollectionFile < StandardError; end
6
+ class InvalidCredentialCollection < StandardError; end
7
+ class InvalidCredentialCollectionFile < InvalidCredentialCollection; end
8
+ class InvalidCredentialCollectionEnv < InvalidCredentialCollection; end
9
+ class InvalidCredentialCollectionFd < InvalidCredentialCollection; end
7
10
  class MissingParameter < StandardError; end
8
11
  class NotImplmented < StandardError; end
9
12
  class InvalidCredentialHash < StandardError; end
@@ -1,3 +1,3 @@
1
1
  module Veil
2
- VERSION = "0.3.0"
2
+ VERSION = "0.3.2"
3
3
  end
@@ -2,17 +2,17 @@ require "spec_helper"
2
2
  require "openssl"
3
3
 
4
4
  describe Veil::Cipher::V2 do
5
- let(:iv64) { Base64.strict_encode64("mondaytuesdaywednesday") }
6
- let(:key64) { Base64.strict_encode64("thursdayfridaysaturdaysundaymonday") }
7
- let(:ciphertext64) { "yhHR8ZVUfzRv+4dvcUFlitdmCS3ybi2dGLofzEF1Ibw=" }
5
+ let(:iv64) { Base64.strict_encode64("mondaytue16bytes") }
6
+ let(:key64) { Base64.strict_encode64("thursdayfridaysaturdaysun32bytes") }
7
+ let(:ciphertext64) { "qS6SRmgOgSta2jSdD60sJmu83cRgy4DUJ2nKZwStrqs=" }
8
8
  let(:plainhash) { { "chef-server": "test-value" } }
9
9
 
10
10
  describe "#new" do
11
11
  it "accepts passed key and iv base64-encoded data" do
12
12
  cipher = described_class.new(iv: iv64, key: key64)
13
13
 
14
- expect(cipher.iv).to eq("mondaytuesdaywednesday")
15
- expect(cipher.key).to eq("thursdayfridaysaturdaysundaymonday")
14
+ expect(cipher.iv).to eq("mondaytue16bytes")
15
+ expect(cipher.key).to eq("thursdayfridaysaturdaysun32bytes")
16
16
  end
17
17
 
18
18
  it "generates key and iv data if none was passed" do
@@ -36,8 +36,8 @@ describe Veil::Cipher::V2 do
36
36
  describe "#to_hash" do
37
37
  it "base64-encodes iv and key" do
38
38
  expected = {
39
- iv: "bW9uZGF5dHVlc2RheXdlZG5lc2RheQ==",
40
- key: "dGh1cnNkYXlmcmlkYXlzYXR1cmRheXN1bmRheW1vbmRheQ==",
39
+ iv: "bW9uZGF5dHVlMTZieXRlcw==",
40
+ key: "dGh1cnNkYXlmcmlkYXlzYXR1cmRheXN1bjMyYnl0ZXM=",
41
41
  type: "Veil::Cipher::V2"
42
42
  }
43
43
  expect(described_class.new(iv: iv64, key: key64).to_hash).to eq(expected)
@@ -0,0 +1,68 @@
1
+ require 'spec_helper'
2
+
3
+ describe Veil::CredentialCollection::ChefSecretsEnv do
4
+ describe "#new" do
5
+ context "env variable is set" do
6
+ let(:var_name) { "CHEF_SECRETS_DATA" }
7
+
8
+ before(:each) do
9
+ ENV[var_name] = '{ "secret_service": { "secret_name": "secret_value" } }'
10
+ end
11
+
12
+ it 'reads the secret from the env var CHEF_SECRETS_DATA' do
13
+ expect(subject.get("secret_service", "secret_name")).to eq("secret_value")
14
+ end
15
+
16
+ context "env variable name is passed" do
17
+ let(:var_name) { "CHEF_SECRETS_DATA_2" }
18
+ let(:subject) { described_class.new(var_name: var_name) }
19
+
20
+ it 'reads the secret from the passed env var name' do
21
+ expect(subject.get("secret_service", "secret_name")).to eq("secret_value")
22
+ end
23
+ end
24
+
25
+ context "env var content cannot be parsed" do
26
+ before(:each) do
27
+ ENV[var_name] = '{ "secre '
28
+ end
29
+
30
+ it "re-raises the JSON parse error" do
31
+ expect{ subject.get("secret_service", "secret_name") }.to raise_error(Veil::InvalidCredentialCollectionEnv)
32
+ end
33
+ end
34
+ end
35
+
36
+ context "env variable is not set" do
37
+ let(:var_name) { "CHEF_SECRETS_DATA" }
38
+
39
+ before(:each) do
40
+ ENV.delete(var_name)
41
+ end
42
+
43
+ it 'raises an exception' do
44
+ expect{ described_class.new }.to raise_error(Veil::InvalidCredentialCollectionEnv)
45
+ end
46
+ end
47
+
48
+ context "unsupported methods" do
49
+ let(:var_name) { "CHEF_SECRETS_DATA" }
50
+
51
+ before(:each) do
52
+ ENV[var_name] = '{}'
53
+ end
54
+
55
+ it 'does not support #rotate' do
56
+ expect{ subject.rotate }.to raise_error(NotImplementedError)
57
+ end
58
+
59
+ it 'does not support #save' do
60
+ expect{ subject.save }.to raise_error(NotImplementedError)
61
+ end
62
+
63
+ it 'does not support #rotate_hasher' do
64
+ expect{ subject.rotate_hasher }.to raise_error(NotImplementedError)
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,64 @@
1
+ require 'spec_helper'
2
+
3
+ describe Veil::CredentialCollection::ChefSecretsFd do
4
+ describe "#new" do
5
+ let(:content) { '{ "secret_service": { "secret_name": "secret_value" } }' }
6
+ let(:content_file) {
7
+ rd, wr = IO.pipe
8
+ wr.puts content
9
+ wr.close
10
+ rd
11
+ }
12
+
13
+ context "env variable is set" do
14
+ before(:each) do
15
+ ENV['CHEF_SECRETS_FD'] = content_file.to_i.to_s
16
+ end
17
+
18
+ it 'reads the secret from the passed file descriptor' do
19
+ expect(subject.get("secret_service", "secret_name")).to eq("secret_value")
20
+ end
21
+
22
+ # TODO(ssd) 2017-03-22: We wanted a test for closing the FD, but
23
+ # didn't want to fight with ruby today.
24
+
25
+ context "env var content cannot be parsed" do
26
+ let(:content) { '{ "secre ' }
27
+
28
+ it "re-raises the JSON parse error" do
29
+ expect{ subject.get("secret_service", "secret_name") }.to raise_error(Veil::InvalidCredentialCollectionFd)
30
+ end
31
+ end
32
+ end
33
+
34
+ context "CHEF_SECRETS_FD is not set" do
35
+ before(:each) do
36
+ ENV.delete('CHEF_SECRETS_FD')
37
+ end
38
+
39
+ it 'raises an exception' do
40
+ expect{ described_class.new }.to raise_error(Veil::InvalidCredentialCollectionFd)
41
+ end
42
+ end
43
+
44
+ context "unsupported methods" do
45
+ let(:content) { '{}' }
46
+
47
+ before(:each) do
48
+ ENV['CHEF_SECRETS_FD'] = content_file.to_i.to_s
49
+ end
50
+
51
+ it 'does not support #rotate' do
52
+ expect{ subject.rotate }.to raise_error(NotImplementedError)
53
+ end
54
+
55
+ it 'does not support #save' do
56
+ expect{ subject.save }.to raise_error(NotImplementedError)
57
+ end
58
+
59
+ it 'does not support #rotate_hasher' do
60
+ expect{ subject.rotate_hasher }.to raise_error(NotImplementedError)
61
+ end
62
+ end
63
+ end
64
+ end
@@ -97,43 +97,97 @@ describe Veil::CredentialCollection::ChefSecretsFile do
97
97
  s
98
98
  end
99
99
 
100
- context "when the user is set" do
101
- it "gives the file proper permissions" do
102
- expect(Tempfile).to receive(:new).with("veil").and_return(tmpfile)
103
- expect(FileUtils).to receive(:chown).with(user, user, "/tmp/unguessable")
104
- expect(FileUtils).to receive(:mv).with("/tmp/unguessable", file.path)
105
-
106
- creds = described_class.new(path: file.path,
107
- user: user)
108
- creds.add("redis_lb", "password")
109
- creds.save
100
+ context "when the target file does not exist" do
101
+
102
+ let(:secrets_file) { double(File) }
103
+ before(:each) do
104
+ allow(File).to receive(:stat).with(file.path).and_raise(Errno::ENOENT)
105
+ end
106
+
107
+ context "when the user is not set" do
108
+ it "does not change any permissions" do
109
+ expect(Tempfile).to receive(:new).with("veil").and_return(tmpfile)
110
+ expect(FileUtils).not_to receive(:chown)
111
+ expect(FileUtils).to receive(:mv).with("/tmp/unguessable", file.path)
112
+
113
+ creds = described_class.new(path: file.path)
114
+ creds.add("redis_lb", "password")
115
+ creds.save
116
+ end
117
+ end
118
+
119
+ context "when the user is set" do
120
+ it "gives the file proper permissions" do
121
+ expect(Tempfile).to receive(:new).with("veil").and_return(tmpfile)
122
+ expect(FileUtils).to receive(:chown).with(user, user, "/tmp/unguessable")
123
+ expect(FileUtils).to receive(:mv).with("/tmp/unguessable", file.path)
124
+
125
+ creds = described_class.new(path: file.path,
126
+ user: user)
127
+ creds.add("redis_lb", "password")
128
+ creds.save
129
+ end
110
130
  end
111
131
  end
112
132
 
113
- context "when user and group are set" do
114
- it "gives the file proper permissions" do
115
- expect(Tempfile).to receive(:new).with("veil").and_return(tmpfile)
116
- expect(FileUtils).to receive(:chown).with(user, group, "/tmp/unguessable")
117
- expect(FileUtils).to receive(:mv).with("/tmp/unguessable", file.path)
118
-
119
- creds = described_class.new(path: file.path,
120
- user: user,
121
- group: group)
122
- creds.add("redis_lb", "password")
123
- creds.save
133
+ context "when the target file exists" do
134
+
135
+ let(:secrets_file) { double(File) }
136
+ let(:secrets_file_stat) { double(File, uid: 100, gid: 1000) }
137
+ before(:each) do
138
+ allow(File).to receive(:stat).with(file.path).and_return(secrets_file_stat)
124
139
  end
125
140
 
126
- it "gives the file proper permission even when called from_file" do
127
- file.puts("{}"); file.rewind
128
- expect(Tempfile).to receive(:new).with("veil").and_return(tmpfile)
129
- expect(FileUtils).to receive(:chown).with(user, group, "/tmp/unguessable")
130
- expect(FileUtils).to receive(:mv).with("/tmp/unguessable", file.path)
131
-
132
- creds = described_class.from_file(file.path,
133
- user: user,
134
- group: group)
135
- creds.add("redis_lb", "password")
136
- creds.save
141
+ context "when the user is not set" do
142
+ it "keeps the existing file permissions" do
143
+ expect(Tempfile).to receive(:new).with("veil").and_return(tmpfile)
144
+ expect(FileUtils).to receive(:chown).with(100, 1000, "/tmp/unguessable")
145
+ expect(FileUtils).to receive(:mv).with("/tmp/unguessable", file.path)
146
+
147
+ creds = described_class.new(path: file.path)
148
+ creds.add("redis_lb", "password")
149
+ creds.save
150
+ end
151
+ end
152
+
153
+ context "when the user is set" do
154
+ it "gives the file proper permissions" do
155
+ expect(Tempfile).to receive(:new).with("veil").and_return(tmpfile)
156
+ expect(FileUtils).to receive(:chown).with(user, user, "/tmp/unguessable")
157
+ expect(FileUtils).to receive(:mv).with("/tmp/unguessable", file.path)
158
+
159
+ creds = described_class.new(path: file.path,
160
+ user: user)
161
+ creds.add("redis_lb", "password")
162
+ creds.save
163
+ end
164
+ end
165
+
166
+ context "when user and group are set" do
167
+ it "gives the file proper permissions" do
168
+ expect(Tempfile).to receive(:new).with("veil").and_return(tmpfile)
169
+ expect(FileUtils).to receive(:chown).with(user, group, "/tmp/unguessable")
170
+ expect(FileUtils).to receive(:mv).with("/tmp/unguessable", file.path)
171
+
172
+ creds = described_class.new(path: file.path,
173
+ user: user,
174
+ group: group)
175
+ creds.add("redis_lb", "password")
176
+ creds.save
177
+ end
178
+
179
+ it "gives the file proper permission even when called from_file" do
180
+ file.puts("{}"); file.rewind
181
+ expect(Tempfile).to receive(:new).with("veil").and_return(tmpfile)
182
+ expect(FileUtils).to receive(:chown).with(user, group, "/tmp/unguessable")
183
+ expect(FileUtils).to receive(:mv).with("/tmp/unguessable", file.path)
184
+
185
+ creds = described_class.from_file(file.path,
186
+ user: user,
187
+ group: group)
188
+ creds.add("redis_lb", "password")
189
+ creds.save
190
+ end
137
191
  end
138
192
  end
139
193
  end
@@ -11,6 +11,15 @@ describe Veil::CredentialCollection do
11
11
  end
12
12
  end
13
13
 
14
+ context 'passing provider "chef-secrets-env"' do
15
+ let(:opts) { { provider: 'chef-secrets-env' } }
16
+
17
+ it 'instantiates ChefSecretsFile with all options' do
18
+ expect(Veil::CredentialCollection::ChefSecretsEnv).to receive(:new).with(opts)
19
+ described_class.from_config(opts)
20
+ end
21
+ end
22
+
14
23
  context 'passing anything else as provider' do
15
24
  let(:opts) { { provider: 'vault' } }
16
25
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: veil
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.3.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chef Software, Inc.
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-03-15 00:00:00.000000000 Z
11
+ date: 2020-04-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bcrypt
@@ -84,8 +84,6 @@ description: Veil is a Ruby Gem for generating secure secrets from a shared secr
84
84
  email:
85
85
  - partnereng@chef.io
86
86
  executables:
87
- - console
88
- - setup
89
87
  - veil-dump-secrets
90
88
  - veil-env-helper
91
89
  - veil-ingest-secret
@@ -105,6 +103,8 @@ files:
105
103
  - lib/veil/credential.rb
106
104
  - lib/veil/credential_collection.rb
107
105
  - lib/veil/credential_collection/base.rb
106
+ - lib/veil/credential_collection/chef_secrets_env.rb
107
+ - lib/veil/credential_collection/chef_secrets_fd.rb
108
108
  - lib/veil/credential_collection/chef_secrets_file.rb
109
109
  - lib/veil/exceptions.rb
110
110
  - lib/veil/hasher.rb
@@ -117,6 +117,8 @@ files:
117
117
  - spec/cipher/v2_spec.rb
118
118
  - spec/cipher_spec.rb
119
119
  - spec/credential_collection/base_spec.rb
120
+ - spec/credential_collection/chef_secrets_env_spec.rb
121
+ - spec/credential_collection/chef_secrets_fd_spec.rb
120
122
  - spec/credential_collection/chef_secrets_file_spec.rb
121
123
  - spec/credential_collection_spec.rb
122
124
  - spec/credential_spec.rb
@@ -127,7 +129,7 @@ files:
127
129
  - spec/spec_helper.rb
128
130
  - spec/utils_spec.rb
129
131
  - spec/veil_spec.rb
130
- homepage: https://github.com/chef/chef-server/
132
+ homepage: https://github.com/chef/chef_secrets/
131
133
  licenses:
132
134
  - Apache-2.0
133
135
  metadata: {}
@@ -146,8 +148,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
146
148
  - !ruby/object:Gem::Version
147
149
  version: '0'
148
150
  requirements: []
149
- rubyforge_project:
150
- rubygems_version: 2.4.5.2
151
+ rubygems_version: 3.0.3
151
152
  signing_key:
152
153
  specification_version: 4
153
154
  summary: Veil is a Ruby Gem for generating secure secrets from a shared secret
@@ -156,6 +157,8 @@ test_files:
156
157
  - spec/cipher/v2_spec.rb
157
158
  - spec/cipher_spec.rb
158
159
  - spec/credential_collection/base_spec.rb
160
+ - spec/credential_collection/chef_secrets_env_spec.rb
161
+ - spec/credential_collection/chef_secrets_fd_spec.rb
159
162
  - spec/credential_collection/chef_secrets_file_spec.rb
160
163
  - spec/credential_collection_spec.rb
161
164
  - spec/credential_spec.rb