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 +5 -5
- data/bin/veil-env-helper +75 -18
- data/lib/veil/credential_collection.rb +6 -0
- data/lib/veil/credential_collection/base.rb +42 -13
- data/lib/veil/credential_collection/chef_secrets_env.rb +43 -0
- data/lib/veil/credential_collection/chef_secrets_fd.rb +57 -0
- data/lib/veil/credential_collection/chef_secrets_file.rb +11 -28
- data/lib/veil/exceptions.rb +4 -1
- data/lib/veil/version.rb +1 -1
- data/spec/cipher/v2_spec.rb +7 -7
- data/spec/credential_collection/chef_secrets_env_spec.rb +68 -0
- data/spec/credential_collection/chef_secrets_fd_spec.rb +64 -0
- data/spec/credential_collection/chef_secrets_file_spec.rb +86 -32
- data/spec/credential_collection_spec.rb +9 -0
- metadata +10 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 3af8aaa1c4ac8737e148057359becae4bc29fcd2e4ba93a5e783f3ea840097e0
|
4
|
+
data.tar.gz: e9de20099a2d0a0288bdbd75f831471d83473ea284dd662b2fc9393bce370432
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4c9f9d837b56c7c1bd19283a8442761ef66ce108a09427ac9a42162c21a44465f26fddeb50480c2c44c9dd7d15890d77574116af854f99ea001d870615e2b013
|
7
|
+
data.tar.gz: cce7ba9c026d5ccce2cdaff51974919a7d8d85d8e97fa97e8f6f8a28c2ce2097798250728e1f94b71506711ce5aeb54cafdcdaf5ceefbd064d28bdcb3e7fc806
|
data/bin/veil-env-helper
CHANGED
@@ -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("
|
18
|
-
options[:
|
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
|
44
|
+
def from_secret_spec(secret, start = {})
|
27
45
|
parts = secret.split("=")
|
28
|
-
case parts.length
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
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
|
-
|
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
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
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
|
226
|
-
hash
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
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
|
-
|
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
|
82
|
-
|
83
|
-
|
84
|
-
|
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
|
data/lib/veil/exceptions.rb
CHANGED
@@ -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
|
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
|
data/lib/veil/version.rb
CHANGED
data/spec/cipher/v2_spec.rb
CHANGED
@@ -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("
|
6
|
-
let(:key64) { Base64.strict_encode64("
|
7
|
-
let(:ciphertext64) { "
|
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("
|
15
|
-
expect(cipher.key).to eq("
|
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: "
|
40
|
-
key: "
|
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
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
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
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
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
|
-
|
127
|
-
file
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
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.
|
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:
|
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/
|
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
|
-
|
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
|