arcanus 0.12.1 → 1.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.
- checksums.yaml +4 -4
- data/lib/arcanus.rb +2 -4
- data/lib/arcanus/chest.rb +23 -7
- data/lib/arcanus/cli.rb +3 -5
- data/lib/arcanus/command/base.rb +5 -11
- data/lib/arcanus/key.rb +2 -2
- data/lib/arcanus/repo.rb +0 -5
- data/lib/arcanus/version.rb +1 -1
- metadata +2 -3
- data/lib/arcanus/configuration.rb +0 -94
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0f7f318dc67cf83cf9a258a8f73db5f8f8055530
|
4
|
+
data.tar.gz: 051293634b012a4d84b09f03e55ac9a041853f1b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f8aef9c0613d3849b8dfef3c8c585f7fe16c91c3647c1670784029819fe1d31005f3574b882a0b30f4f7faaa505a2171218bf27742decf5094c74690df49b795
|
7
|
+
data.tar.gz: 6c468609ff2cc18b8fa9d4b4cce904c662df53c188dc2f172ec3d3c1c2b15942221f0c5476c8e2ceea3296006a538634b4f93a09475d61e9f5d9f2fca8a93196
|
data/lib/arcanus.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
require 'arcanus/constants'
|
2
2
|
require 'arcanus/errors'
|
3
3
|
require 'arcanus/error_handler'
|
4
|
-
require 'arcanus/configuration'
|
5
4
|
require 'arcanus/input'
|
6
5
|
require 'arcanus/output'
|
7
6
|
require 'arcanus/ui'
|
@@ -27,9 +26,8 @@ module Arcanus
|
|
27
26
|
# Loads Arcanus chest and decrypts secrets.
|
28
27
|
#
|
29
28
|
# @param directory [String] repo directory
|
30
|
-
def load
|
31
|
-
@
|
32
|
-
@repo = Repo.new(@config)
|
29
|
+
def load
|
30
|
+
@repo = Repo.new
|
33
31
|
|
34
32
|
unless File.directory?(@repo.arcanus_dir)
|
35
33
|
raise Errors::UsageError,
|
data/lib/arcanus/chest.rb
CHANGED
@@ -7,6 +7,7 @@ module Arcanus
|
|
7
7
|
# Encapsulates the collection of encrypted secrets managed by Arcanus.
|
8
8
|
class Chest # rubocop:disable Metrics/ClassLength
|
9
9
|
SIGNATURE_SIZE_BITS = 256
|
10
|
+
ENCRYPTION_CIPHER = OpenSSL::Cipher.new('AES-256-CBC')
|
10
11
|
|
11
12
|
def initialize(key:, chest_file_path:)
|
12
13
|
@key = key
|
@@ -157,7 +158,14 @@ module Arcanus
|
|
157
158
|
|
158
159
|
def encrypt_value(value)
|
159
160
|
dumped_value = Marshal.dump(value)
|
160
|
-
|
161
|
+
|
162
|
+
# Create a random symmetric key so we can encrypt plaintext of arbitrary length
|
163
|
+
sym_key = ENCRYPTION_CIPHER.reset.encrypt.random_key
|
164
|
+
iv = Base64.encode64(ENCRYPTION_CIPHER.random_iv)
|
165
|
+
enc_sym_key = Base64.encode64(@key.encrypt(sym_key))
|
166
|
+
encrypted_value = Base64.encode64(ENCRYPTION_CIPHER.update(dumped_value) +
|
167
|
+
ENCRYPTION_CIPHER.final)
|
168
|
+
|
161
169
|
salt = SecureRandom.hex(8)
|
162
170
|
|
163
171
|
signature = Digest::SHA2.new(SIGNATURE_SIZE_BITS).tap do |digest|
|
@@ -165,23 +173,31 @@ module Arcanus
|
|
165
173
|
digest << dumped_value
|
166
174
|
end.to_s
|
167
175
|
|
168
|
-
"#{encrypted_value}:#{salt}:#{signature}"
|
176
|
+
"#{iv}:#{enc_sym_key}:#{encrypted_value}:#{salt}:#{signature}"
|
169
177
|
end
|
170
178
|
|
171
|
-
def decrypt_value(blob)
|
179
|
+
def decrypt_value(blob) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/MethodLength
|
172
180
|
unless blob.is_a?(String)
|
173
181
|
raise Errors::DecryptionError,
|
174
182
|
"Expecting an encrypted blob but got '#{blob}'"
|
175
183
|
end
|
176
184
|
|
177
|
-
|
185
|
+
iv_b64, enc_sym_key_b64, encrypted_value_b64, salt, signature = blob.split(':')
|
178
186
|
|
179
|
-
if signature.nil? || salt.nil? ||
|
187
|
+
if signature.nil? || salt.nil? || encrypted_value_b64.nil? ||
|
188
|
+
enc_sym_key_b64.nil? || iv_b64.nil?
|
180
189
|
raise Errors::DecryptionError,
|
181
|
-
"Invalid blob format '#{blob}'
|
190
|
+
"Invalid blob format '#{blob}'. " \
|
191
|
+
'Did you encrypt this with an older version of Arcanus?'
|
182
192
|
end
|
183
193
|
|
184
|
-
|
194
|
+
iv = Base64.decode64(iv_b64)
|
195
|
+
sym_key = @key.decrypt(Base64.decode64(enc_sym_key_b64))
|
196
|
+
ENCRYPTION_CIPHER.reset.decrypt
|
197
|
+
ENCRYPTION_CIPHER.iv = iv
|
198
|
+
ENCRYPTION_CIPHER.key = sym_key
|
199
|
+
dumped_value = ENCRYPTION_CIPHER.update(Base64.decode64(encrypted_value_b64)) +
|
200
|
+
ENCRYPTION_CIPHER.final
|
185
201
|
|
186
202
|
actual_signature = Digest::SHA2.new(SIGNATURE_SIZE_BITS).tap do |digest|
|
187
203
|
digest << salt
|
data/lib/arcanus/cli.rb
CHANGED
@@ -28,8 +28,7 @@ module Arcanus
|
|
28
28
|
# @param [Array<String>] arguments
|
29
29
|
# @return [Integer] exit status code
|
30
30
|
def run(arguments)
|
31
|
-
|
32
|
-
run_command(config, arguments)
|
31
|
+
run_command(arguments)
|
33
32
|
|
34
33
|
ExitCodes::OK
|
35
34
|
rescue => ex
|
@@ -40,15 +39,14 @@ module Arcanus
|
|
40
39
|
|
41
40
|
# Executes the appropriate command given the list of command line arguments.
|
42
41
|
#
|
43
|
-
# @param config [Arcanus::Configuration]
|
44
42
|
# @param ui [Arcanus::UI]
|
45
43
|
# @param arguments [Array<String>]
|
46
44
|
# @raise [Arcanus::Errors::ArcanusError] when any exceptional circumstance occurs
|
47
|
-
def run_command(
|
45
|
+
def run_command(arguments)
|
48
46
|
arguments = convert_arguments(arguments)
|
49
47
|
|
50
48
|
require 'arcanus/command/base'
|
51
|
-
Command::Base.from_arguments(
|
49
|
+
Command::Base.from_arguments(@ui, arguments).run
|
52
50
|
end
|
53
51
|
|
54
52
|
def convert_arguments(arguments)
|
data/lib/arcanus/command/base.rb
CHANGED
@@ -8,12 +8,11 @@ module Arcanus::Command
|
|
8
8
|
class << self
|
9
9
|
# Create a command from a list of arguments.
|
10
10
|
#
|
11
|
-
# @param config [Arcanus::Configuration]
|
12
11
|
# @param ui [Arcanus::UI]
|
13
12
|
# @param arguments [Array<String>]
|
14
13
|
# @return [Arcanus::Command::Base] appropriate command for the given
|
15
14
|
# arguments
|
16
|
-
def from_arguments(
|
15
|
+
def from_arguments(ui, arguments)
|
17
16
|
cmd = arguments.first
|
18
17
|
|
19
18
|
begin
|
@@ -23,7 +22,7 @@ module Arcanus::Command
|
|
23
22
|
"`arcanus #{cmd}` is not a valid command"
|
24
23
|
end
|
25
24
|
|
26
|
-
Arcanus::Command.const_get(Arcanus::Utils.camel_case(cmd)).new(
|
25
|
+
Arcanus::Command.const_get(Arcanus::Utils.camel_case(cmd)).new(ui, arguments)
|
27
26
|
end
|
28
27
|
|
29
28
|
def description(desc = nil)
|
@@ -36,11 +35,9 @@ module Arcanus::Command
|
|
36
35
|
end
|
37
36
|
end
|
38
37
|
|
39
|
-
# @param config [Arcanus::Configuration]
|
40
38
|
# @param ui [Arcanus::UI]
|
41
39
|
# @param arguments [Array<String>]
|
42
|
-
def initialize(
|
43
|
-
@config = config
|
40
|
+
def initialize(ui, arguments)
|
44
41
|
@ui = ui
|
45
42
|
@arguments = arguments
|
46
43
|
end
|
@@ -61,7 +58,7 @@ module Arcanus::Command
|
|
61
58
|
#
|
62
59
|
# @param command_arguments [Array<String>]
|
63
60
|
def execute_command(command_arguments)
|
64
|
-
self.class.from_arguments(
|
61
|
+
self.class.from_arguments(ui, command_arguments).execute
|
65
62
|
end
|
66
63
|
|
67
64
|
private
|
@@ -69,9 +66,6 @@ module Arcanus::Command
|
|
69
66
|
# @return [Array<String>]
|
70
67
|
attr_reader :arguments
|
71
68
|
|
72
|
-
# @return [Arcanus::Configuration]
|
73
|
-
attr_reader :config
|
74
|
-
|
75
69
|
# @return [Arcanus::UI]
|
76
70
|
attr_reader :ui
|
77
71
|
|
@@ -79,7 +73,7 @@ module Arcanus::Command
|
|
79
73
|
#
|
80
74
|
# @return [Arcanus::Repo]
|
81
75
|
def repo
|
82
|
-
@repo ||= Arcanus::Repo.new
|
76
|
+
@repo ||= Arcanus::Repo.new
|
83
77
|
end
|
84
78
|
|
85
79
|
# Execute a process and return the result including status and output.
|
data/lib/arcanus/key.rb
CHANGED
@@ -4,7 +4,7 @@ module Arcanus
|
|
4
4
|
# Encapsulates operations for creating keys that encrypt/decrypt secrets.
|
5
5
|
class Key
|
6
6
|
DEFAULT_SIZE = 4096
|
7
|
-
|
7
|
+
PEM_PASSWORD_CIPHER = OpenSSL::Cipher.new('AES-256-CBC')
|
8
8
|
|
9
9
|
class << self
|
10
10
|
def generate(key_size_bits: DEFAULT_SIZE)
|
@@ -36,7 +36,7 @@ module Arcanus
|
|
36
36
|
def save(key_file_path:, password: nil)
|
37
37
|
pem =
|
38
38
|
if password
|
39
|
-
@key.to_pem(
|
39
|
+
@key.to_pem(PEM_PASSWORD_CIPHER, password)
|
40
40
|
else
|
41
41
|
@key.to_pem
|
42
42
|
end
|
data/lib/arcanus/repo.rb
CHANGED
@@ -3,11 +3,6 @@ require 'pathname'
|
|
3
3
|
module Arcanus
|
4
4
|
# Exposes information about the current git repository.
|
5
5
|
class Repo
|
6
|
-
# @param config [Arcanus::Configuration]
|
7
|
-
def initialize(config)
|
8
|
-
@config = config
|
9
|
-
end
|
10
|
-
|
11
6
|
# Returns the absolute path to the root of the current repository the
|
12
7
|
# current working directory resides within.
|
13
8
|
#
|
data/lib/arcanus/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: arcanus
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Shane da Silva
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-11-
|
11
|
+
date: 2016-11-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: childprocess
|
@@ -74,7 +74,6 @@ files:
|
|
74
74
|
- lib/arcanus/command/show.rb
|
75
75
|
- lib/arcanus/command/unlock.rb
|
76
76
|
- lib/arcanus/command/version.rb
|
77
|
-
- lib/arcanus/configuration.rb
|
78
77
|
- lib/arcanus/constants.rb
|
79
78
|
- lib/arcanus/error_handler.rb
|
80
79
|
- lib/arcanus/errors.rb
|
@@ -1,94 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'pathname'
|
4
|
-
require 'yaml'
|
5
|
-
|
6
|
-
module Arcanus
|
7
|
-
# Stores runtime configuration for the application.
|
8
|
-
#
|
9
|
-
# This is intended to define helper methods for accessing configuration so
|
10
|
-
# this logic can be shared amongst the various components of the system.
|
11
|
-
class Configuration
|
12
|
-
# Name of the configuration file.
|
13
|
-
FILE_NAME = 'config.yaml'.freeze
|
14
|
-
|
15
|
-
class << self
|
16
|
-
# Loads appropriate configuration file given the current working
|
17
|
-
# directory.
|
18
|
-
#
|
19
|
-
# @return [Arcanus::Configuration]
|
20
|
-
def load_applicable(working_directory = Dir.pwd)
|
21
|
-
current_directory = File.expand_path(working_directory)
|
22
|
-
config_file = applicable_config_file(current_directory)
|
23
|
-
|
24
|
-
if config_file
|
25
|
-
from_file(config_file)
|
26
|
-
else
|
27
|
-
# No configuration file, so assume defaults
|
28
|
-
new({})
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
# Loads a configuration from a file.
|
33
|
-
#
|
34
|
-
# @return [Arcanus::Configuration]
|
35
|
-
def from_file(config_file)
|
36
|
-
options =
|
37
|
-
if yaml = YAML.load_file(config_file)
|
38
|
-
yaml.to_hash
|
39
|
-
else
|
40
|
-
{}
|
41
|
-
end
|
42
|
-
|
43
|
-
new(options)
|
44
|
-
end
|
45
|
-
|
46
|
-
private
|
47
|
-
|
48
|
-
# Returns the first valid configuration file found, starting from the
|
49
|
-
# current working directory and ascending to ancestor directories.
|
50
|
-
#
|
51
|
-
# @param directory [String]
|
52
|
-
# @return [String, nil]
|
53
|
-
def applicable_config_file(directory)
|
54
|
-
Pathname.new(directory)
|
55
|
-
.enum_for(:ascend)
|
56
|
-
.map { |dir| dir + '.arcanus' + FILE_NAME }
|
57
|
-
.find do |config_file|
|
58
|
-
config_file if config_file.exist?
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
# Creates a configuration from the given options hash.
|
64
|
-
#
|
65
|
-
# @param options [Hash]
|
66
|
-
def initialize(options)
|
67
|
-
@options = options
|
68
|
-
end
|
69
|
-
|
70
|
-
# Access the configuration as if it were a hash.
|
71
|
-
#
|
72
|
-
# @param key [String, Symbol]
|
73
|
-
# @return [Array, Hash, Number, String]
|
74
|
-
def [](key)
|
75
|
-
@options[key.to_s]
|
76
|
-
end
|
77
|
-
|
78
|
-
# Access the configuration as if it were a hash.
|
79
|
-
#
|
80
|
-
# @param key [String, Symbol]
|
81
|
-
# @return [Array, Hash, Number, String]
|
82
|
-
def fetch(key, *args)
|
83
|
-
@options.fetch(key.to_s, *args)
|
84
|
-
end
|
85
|
-
|
86
|
-
# Compares this configuration with another.
|
87
|
-
#
|
88
|
-
# @param other [HamlLint::Configuration]
|
89
|
-
# @return [true,false] whether the given configuration is equivalent
|
90
|
-
def ==(other)
|
91
|
-
super || @options == other.instance_variable_get('@options')
|
92
|
-
end
|
93
|
-
end
|
94
|
-
end
|