encipher 0.0.1a
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.env +5 -0
- data/.gitignore +25 -0
- data/.travis.yml +6 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +97 -0
- data/Rakefile +2 -0
- data/bin/encipher +5 -0
- data/config/rubocop.yml +47 -0
- data/encipher.gemspec +40 -0
- data/lib/encipher.rb +14 -0
- data/lib/encipher/cli/base.rb +41 -0
- data/lib/encipher/cli/cli.rb +86 -0
- data/lib/encipher/cli/convert.rb +26 -0
- data/lib/encipher/dotenv.rb +20 -0
- data/lib/encipher/encipher.rb +42 -0
- data/lib/encipher/environment.rb +73 -0
- data/lib/encipher/models/secret.rb +32 -0
- data/lib/encipher/models/user.rb +13 -0
- data/lib/encipher/security.rb +41 -0
- data/lib/encipher/vault.rb +123 -0
- data/lib/encipher/version.rb +3 -0
- data/secrets.db +0 -0
- data/spec/enchiper/cli/base_spec.rb +70 -0
- data/spec/spec_helper.rb +82 -0
- metadata +296 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 2960852e4181fe772df6e890cb788745f1b024b7
|
4
|
+
data.tar.gz: 616cfc3a33a901aea4a816ab5f45c3705c69e8e4
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 82ab87765e53556bfd026d5a9ee40dfa3b86a391f5773c5462104b6052527d9c92972f3301fceee6d0d6a2deec4eaa2d619cf176df8f6d694ab728e6276e5d52
|
7
|
+
data.tar.gz: 1c10702862f9fc8eb685b37d0c8e34048446231df372cd21df1f756258817586f29a5779f86f3f880f1176940a637d204b5061e3f7e91aca501e812775816ea1
|
data/.env
ADDED
data/.gitignore
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
.yardoc
|
6
|
+
Gemfile.lock
|
7
|
+
InstalledFiles
|
8
|
+
_yardoc
|
9
|
+
coverage
|
10
|
+
doc/
|
11
|
+
lib/bundler/man
|
12
|
+
pkg
|
13
|
+
rdoc
|
14
|
+
spec/reports
|
15
|
+
test/tmp
|
16
|
+
test/version_tmp
|
17
|
+
tmp
|
18
|
+
*.bundle
|
19
|
+
*.so
|
20
|
+
*.o
|
21
|
+
*.a
|
22
|
+
mkmf.log
|
23
|
+
.DS_Store*
|
24
|
+
.encipher
|
25
|
+
developers.yml
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Joey Lorich
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
# Encipher
|
2
|
+
|
3
|
+
An RSA ssh-key encryption based configuration storage solution.
|
4
|
+
|
5
|
+
Keeping configuration information in the environment is one of the tenants of a [twelve-factor app](http://12factor.net/config), however configuring each server individually can be time consuming and when configuration information needs to change often lots of work must be done.
|
6
|
+
|
7
|
+
Encipher provides a method to simplify this situation by keeping enviroment and user restricted, public-key encrypted, secrets right in the repository. On application load Encipher decrepts the secrets for the appropriate environment using a private key held on the server and stores them in memory.
|
8
|
+
|
9
|
+
####Really? Secrets in the repository?
|
10
|
+
|
11
|
+
Yes. In general, secret information should not be kept in a source repository. Each developer with permission to clone a repository should not necessarily have permission to deploy or edit configuration information. However, all Encipher-kept secrets are only decryptable by a user with a valid, enrolled, private key that has had a certain environments secrets enabled by an authorized user.
|
12
|
+
|
13
|
+
####How it works
|
14
|
+
Each encipher user and server has a public ssh key enrolled by another authroized user for a given environment (the initial secrets database creation enrolls the first user). The enrolled user's public key is then stored for future use. When a secret is added or modified, an entry is created and encrypted with each enrolled key. Each user can *only* decrypt secrets encrypted with their public key, so limiting a key's access to a certain set of secrets is easy.
|
15
|
+
|
16
|
+
|
17
|
+
## Installation
|
18
|
+
|
19
|
+
Add this line to your application's Gemfile:
|
20
|
+
|
21
|
+
gem 'encipher', git: git://github.com/jlorich/encipher.git
|
22
|
+
|
23
|
+
And then execute:
|
24
|
+
|
25
|
+
$ bundle
|
26
|
+
|
27
|
+
|
28
|
+
## Environment-based restrictions
|
29
|
+
All actions are by default segregated by encipher-environment. Each key must be enrolled in each environment to be able to read or set secrets for it. The enviorment is by default `:development`, but may be set by specifiying the `ENCIPHER_ENV` enviornment variable. The enviornment may also be set in code by calling `Encipher.env = :my_env`.
|
30
|
+
|
31
|
+
All secrets set will only be encrypted for keys enrolled in the given environment.
|
32
|
+
|
33
|
+
|
34
|
+
## Command line usage
|
35
|
+
|
36
|
+
Initialize encipher with your desired private key (this location will be cached in the .encipher file)
|
37
|
+
|
38
|
+
encipher init
|
39
|
+
|
40
|
+
You can also specify the key path with
|
41
|
+
|
42
|
+
encipher init /path/to/your/private_key
|
43
|
+
|
44
|
+
#####Enroll yourself. Your public key will be auto-generated from the private key used with encipher init.
|
45
|
+
|
46
|
+
encipher enroll
|
47
|
+
|
48
|
+
#####Enroll a new user or servers key
|
49
|
+
|
50
|
+
encipher enroll ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDCvRm/KvZy8iDyDgjokMMMMUz8UK84OlBGbeeo6VT8UZc6e8E1xUNfFCNp6xUQMO8N+vpqxlOr3haAXn6sdHCnMb8BpWYwq0Un19WaToTiv/15tRvZzkQw9KPu/TjBy8OaSjNAAxo5rWkJbDc0K4WdBtDJ4uk3i+UmxpYXhOtC9eCLgMdxZ6xIWuP0ymyYe/SZSdupeKblaehYyFEb2NKTVbvnbef79ZogJG7IlWFYS+qaqk+xFZIRYEX3yLIijF/WvLmZw2NVurb8rhnNLNDI3v+Gy+bw0sitZKvX5MWunpmykFY8/5YnWuEA1AJGaexC/1EWXUzVN6o2my4Aqlwz
|
51
|
+
|
52
|
+
#####To store a secret:
|
53
|
+
|
54
|
+
encipher set "secret name" "this is really cool"
|
55
|
+
|
56
|
+
#####To retreive a secret:
|
57
|
+
|
58
|
+
encipher get "secret name"
|
59
|
+
|
60
|
+
#####To list all enrolled secrets:
|
61
|
+
|
62
|
+
encipher list
|
63
|
+
|
64
|
+
## Easy Editing
|
65
|
+
For ease of editing, Encipher also provides the `edit` command line option.
|
66
|
+
|
67
|
+
encipher edit
|
68
|
+
|
69
|
+
Calling `edit` will launch an external editor (specified by the `EDITOR` environment variable, and by default `vi` on most systems) populated with the entire secrets hash in [YAML](http://www.yaml.org/) for the current environment. Once saved, all specified secrets will be re-encrypted for all appropriately enrolled keys and the tempfile used for editing will be destroyed.
|
70
|
+
|
71
|
+
## Programmatic usage
|
72
|
+
|
73
|
+
##### Configuration
|
74
|
+
Each application can be configured as follows:
|
75
|
+
|
76
|
+
Encipher.configure do |config|
|
77
|
+
config.keypath = '~/.ssh/my_server_key'
|
78
|
+
config.env = :staging
|
79
|
+
end
|
80
|
+
|
81
|
+
##### Secrets
|
82
|
+
|
83
|
+
All secrets are accessabile by calling
|
84
|
+
|
85
|
+
Encipher.secrets
|
86
|
+
|
87
|
+
This returns a [Recursive OpenStruct](https://github.com/aetherknight/recursive-open-struct) (a hash deeply accessible by string, symbol, or dot notation) containing all secrets information for the specified environment. Once loaded the information is cached in-memory so all subsequent calls to `secrets` won't need to re-decrypt each secret.
|
88
|
+
|
89
|
+
If secrets reloading is desired `Encipher.reload` can be called, or `Encipher.secrets(reload: true)` can be referenced to force reloading.
|
90
|
+
|
91
|
+
## Contributing
|
92
|
+
|
93
|
+
1. Fork it ( https://github.com/jlorich/encipher/fork )
|
94
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
95
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
96
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
97
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
data/bin/encipher
ADDED
data/config/rubocop.yml
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
Documentation:
|
2
|
+
Enabled: false
|
3
|
+
|
4
|
+
EmptyLinesAroundBody:
|
5
|
+
Enabled: false
|
6
|
+
|
7
|
+
LineLength:
|
8
|
+
Max: 120
|
9
|
+
Exclude:
|
10
|
+
|
11
|
+
ClassLength:
|
12
|
+
Enabled: true
|
13
|
+
CountComments: false
|
14
|
+
Max: 150
|
15
|
+
|
16
|
+
MethodLength:
|
17
|
+
Enabled: false
|
18
|
+
|
19
|
+
WhileUntilModifier:
|
20
|
+
Enabled: false
|
21
|
+
|
22
|
+
ClassAndModuleChildren:
|
23
|
+
Enabled: false
|
24
|
+
|
25
|
+
Rails/ActionFilter:
|
26
|
+
Enabled: false
|
27
|
+
|
28
|
+
Rails/DefaultScope:
|
29
|
+
Enabled: false
|
30
|
+
|
31
|
+
Rails/HasAndBelongsToMany:
|
32
|
+
Enabled: false
|
33
|
+
|
34
|
+
Rails/Output:
|
35
|
+
Enabled: false
|
36
|
+
|
37
|
+
Rails/ReadWriteAttribute:
|
38
|
+
Enabled: false
|
39
|
+
|
40
|
+
Rails/ScopeArgs:
|
41
|
+
Enabled: false
|
42
|
+
|
43
|
+
Rails/Validation:
|
44
|
+
Enabled: false
|
45
|
+
|
46
|
+
Rails/Delegate:
|
47
|
+
Enabled: false
|
data/encipher.gemspec
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'encipher/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "encipher"
|
8
|
+
spec.version = Encipher::VERSION
|
9
|
+
spec.authors = ["Joey Lorich"]
|
10
|
+
spec.email = ["joseph@lorich.me"]
|
11
|
+
spec.summary = %q{Secure secrets management}
|
12
|
+
spec.description = %q{Secure secrets management description!}
|
13
|
+
spec.homepage = ""
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.6"
|
22
|
+
spec.add_development_dependency 'rake', '~> 10.3.2'
|
23
|
+
spec.add_development_dependency 'pry-byebug', '~> 1.3.3'
|
24
|
+
spec.add_development_dependency 'rspec', '~> 3.0.0'
|
25
|
+
spec.add_development_dependency 'clint_eastwood', '~> 0.0.1'
|
26
|
+
|
27
|
+
spec.add_dependency 'thor', '~> 0.19.1'
|
28
|
+
spec.add_dependency 'highline', '~> 1.6.21'
|
29
|
+
spec.add_dependency 'deep_merge', '~> 1.0.1'
|
30
|
+
spec.add_dependency 'net-ssh', '~> 2.9.1'
|
31
|
+
spec.add_dependency 'sqlite3', '~> 1.3.9'
|
32
|
+
spec.add_dependency 'data_mapper', '~> 1.2.0'
|
33
|
+
spec.add_dependency 'dm-sqlite-adapter', '~> 1.2.0'
|
34
|
+
spec.add_dependency 'dot_configure', '~> 0.0.1'
|
35
|
+
spec.add_dependency 'exedit', '~> 0.0.2'
|
36
|
+
spec.add_dependency 'awesome_print', '~> 1.2.0'
|
37
|
+
spec.add_dependency 'recursive-open-struct', '~> 0.5.0'
|
38
|
+
|
39
|
+
spec.executables = ['encipher']
|
40
|
+
end
|
data/lib/encipher.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'encipher/version'
|
2
|
+
require 'encipher/security'
|
3
|
+
require 'encipher/vault'
|
4
|
+
require 'encipher/environment'
|
5
|
+
require 'encipher/models/secret'
|
6
|
+
require 'encipher/models/user'
|
7
|
+
require 'encipher/encipher'
|
8
|
+
|
9
|
+
require 'sqlite3'
|
10
|
+
require 'rubygems'
|
11
|
+
require 'base64'
|
12
|
+
require 'data_mapper'
|
13
|
+
require 'net/ssh'
|
14
|
+
require 'pry'
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'thor'
|
2
|
+
require 'yaml'
|
3
|
+
require 'ostruct'
|
4
|
+
require 'encipher/vault'
|
5
|
+
require 'encipher/environment'
|
6
|
+
|
7
|
+
module Encipher
|
8
|
+
module Cli
|
9
|
+
|
10
|
+
# Base CLI commands
|
11
|
+
class Base < Thor
|
12
|
+
protected
|
13
|
+
|
14
|
+
# Loads and configures encipher
|
15
|
+
def environment
|
16
|
+
return @environment if defined? @environment
|
17
|
+
|
18
|
+
@environment = Encipher::Environment.new(config.key_path)
|
19
|
+
end
|
20
|
+
|
21
|
+
def vault
|
22
|
+
return @vault if defined? @vault
|
23
|
+
|
24
|
+
@vault = Encipher::Vault.new(config.key_path)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Loads and returns the config file (memoized)
|
28
|
+
def config
|
29
|
+
return @config if defined? @config
|
30
|
+
|
31
|
+
config_path = File.expand_path './.encipher'
|
32
|
+
|
33
|
+
unless File.exist? config_path
|
34
|
+
fail 'You must run encipher init before running'
|
35
|
+
end
|
36
|
+
|
37
|
+
@config = OpenStruct.new YAML.load(File.read(config_path))
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'encipher/cli/base'
|
2
|
+
require 'encipher/cli/convert'
|
3
|
+
require 'exedit'
|
4
|
+
require 'json'
|
5
|
+
require 'awesome_print'
|
6
|
+
require 'pry'
|
7
|
+
|
8
|
+
module Encipher
|
9
|
+
module Cli
|
10
|
+
# Encipher command line interface
|
11
|
+
class Cli < Base
|
12
|
+
desc 'get [key]', 'Returns an unencrypted value'
|
13
|
+
def get(key)
|
14
|
+
puts environment.get key
|
15
|
+
end
|
16
|
+
|
17
|
+
desc 'set [key] [value]', 'Set an encrypted value'
|
18
|
+
def set(key, value)
|
19
|
+
environment.set key, value
|
20
|
+
end
|
21
|
+
|
22
|
+
desc 'list', 'Lists all secrets for the current environment'
|
23
|
+
def list
|
24
|
+
puts environment.list
|
25
|
+
end
|
26
|
+
|
27
|
+
desc 'edit', 'Edit all secrets in an external editor'
|
28
|
+
def edit
|
29
|
+
result = Exedit.edit environment.hash.to_yaml
|
30
|
+
|
31
|
+
environment.hash = YAML.load(result)
|
32
|
+
end
|
33
|
+
|
34
|
+
desc 'keys', 'Lists all enrolled keys'
|
35
|
+
def keys
|
36
|
+
environment.user_hash.each do |id, ssh_key|
|
37
|
+
wrapped = wrap(ssh_key, 70)
|
38
|
+
|
39
|
+
if wrapped.count > 1
|
40
|
+
puts "#{id.to_s.rjust(10)} - #{wrapped.shift}"
|
41
|
+
|
42
|
+
wrapped.each do |string|
|
43
|
+
puts " #{string}"
|
44
|
+
end
|
45
|
+
else
|
46
|
+
puts puts "#{id} - #{ssh_key}"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
desc 'convert [type]', 'Converts various password stores to encipher'
|
52
|
+
subcommand 'convert', Convert
|
53
|
+
|
54
|
+
desc 'init', 'loads they key at the given keypath'
|
55
|
+
def init(key_path = '~/.ssh/id_rsa')
|
56
|
+
key_path = File.expand_path key_path
|
57
|
+
config_path = File.expand_path './.encipher'
|
58
|
+
|
59
|
+
fail '.encipher file already exists' if File.exist? config_path
|
60
|
+
fail 'Specified key path does not exist' unless File.exist? key_path
|
61
|
+
|
62
|
+
File.open(config_path, 'w') do |file|
|
63
|
+
file.write({ key_path: key_path }.to_yaml)
|
64
|
+
end
|
65
|
+
|
66
|
+
puts 'Encipher is ready for use'
|
67
|
+
end
|
68
|
+
|
69
|
+
desc 'enroll', 'Enrolls a new public key'
|
70
|
+
def enroll(key = nil)
|
71
|
+
vault.enroll(key)
|
72
|
+
end
|
73
|
+
|
74
|
+
desc 'revoke', 'Destroys a user and all their secrets'
|
75
|
+
def revoke(user_id)
|
76
|
+
vault.revoke(user_id)
|
77
|
+
end
|
78
|
+
|
79
|
+
protected
|
80
|
+
|
81
|
+
def wrap(string, width)
|
82
|
+
string.chars.each_slice(width).map(&:join)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'encipher/cli/base'
|
2
|
+
|
3
|
+
module Encipher
|
4
|
+
module Cli
|
5
|
+
# Encipher conversions
|
6
|
+
class Convert < Base
|
7
|
+
# desc 'dotenv', 'Loads a .env file to secrets.db'
|
8
|
+
# def dotenv(path='./.env')
|
9
|
+
# path = File.expand_path path
|
10
|
+
|
11
|
+
# if !File.exist? path
|
12
|
+
# fail Exception.new 'A valid .env file must exist'
|
13
|
+
# end
|
14
|
+
|
15
|
+
# loader = Dotenv.new(path, encipher)
|
16
|
+
# loader.store
|
17
|
+
|
18
|
+
# if loader.secrets.count == 0
|
19
|
+
# puts 'No secrets stored'
|
20
|
+
# else
|
21
|
+
# puts "#{loader.secrets.count} secrets loaded"
|
22
|
+
# end
|
23
|
+
# end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# Encipher
|
2
|
+
module Encipher
|
3
|
+
# Dotenv parser
|
4
|
+
class Dotenv
|
5
|
+
def initialize(path, encipher)
|
6
|
+
@dotenv = YAML.load(File.read(path))
|
7
|
+
@encipher = encipher
|
8
|
+
end
|
9
|
+
|
10
|
+
def secrets
|
11
|
+
@dotenv
|
12
|
+
end
|
13
|
+
|
14
|
+
def store
|
15
|
+
@dotenv.each do |name, secret|
|
16
|
+
@encipher.store name, secret
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'dot_configure'
|
2
|
+
require 'recursive-open-struct'
|
3
|
+
|
4
|
+
# Encipher secrets storage
|
5
|
+
module Encipher
|
6
|
+
extend DotConfigure
|
7
|
+
|
8
|
+
# Requires a key or array of keys to be present, otherwise throw an exception
|
9
|
+
def self.require(keys)
|
10
|
+
[*keys].each do |key|
|
11
|
+
fail "Encipher key (#{key}) not found" unless secrets.keys.include? key.to_s
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# Gets the current encipher environment
|
16
|
+
# Precedence: Manually set env, ENCIPHER_ENV environment variable, options env, default
|
17
|
+
def self.env
|
18
|
+
@env ||= if ENV['ENCIPHER_ENV']
|
19
|
+
ENV['ENCIPHER_ENV'].to_sym
|
20
|
+
else
|
21
|
+
(options.env ? options.env.to_sym : nil) || :development
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# Manually set the encipher env
|
26
|
+
def self.env=(env)
|
27
|
+
@env = env.to_sym
|
28
|
+
end
|
29
|
+
|
30
|
+
# Loads, caches, and returns all available secrets
|
31
|
+
# @param reload [Boolean] Forces the secrets to reload
|
32
|
+
def self.secrets(reload: false)
|
33
|
+
return @secrets if @secrets && !reload
|
34
|
+
environment = Encipher::Environment.new(options.keypath)
|
35
|
+
@secrets = RecursiveOpenStruct.new(environment.hash)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Forces a secrets reload
|
39
|
+
def self.reload
|
40
|
+
secrets(reload: true)
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'encipher'
|
3
|
+
require 'encipher/vault'
|
4
|
+
require 'recursive-open-struct'
|
5
|
+
|
6
|
+
module Encipher
|
7
|
+
# Enviornment variable storage
|
8
|
+
class Environment < Vault
|
9
|
+
|
10
|
+
# Store a new environment variable
|
11
|
+
def set(name, value)
|
12
|
+
require_user
|
13
|
+
|
14
|
+
# Delete all existing variables of that name
|
15
|
+
where(type: :env, name: name).map(&:destroy)
|
16
|
+
|
17
|
+
User.each do |user|
|
18
|
+
lock(Secret.new(
|
19
|
+
user: user,
|
20
|
+
unlocked_value: {
|
21
|
+
env: Encipher.env,
|
22
|
+
type: :env,
|
23
|
+
name: name,
|
24
|
+
value: value
|
25
|
+
}
|
26
|
+
)).save
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def get(name)
|
31
|
+
require_user
|
32
|
+
|
33
|
+
env = unlock(where(type: :env, name: name).first).unlocked_value
|
34
|
+
env[:value]
|
35
|
+
end
|
36
|
+
|
37
|
+
def list
|
38
|
+
require_user
|
39
|
+
where(type: :env).map { |s| unlock(s).unlocked_value[:name] }
|
40
|
+
end
|
41
|
+
|
42
|
+
def hash
|
43
|
+
all.each_with_object({}) do |secret, result|
|
44
|
+
result[secret[:name]] = secret[:value]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def hash=(hash)
|
49
|
+
hash.each do |key, value|
|
50
|
+
set(key, value)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def user_hash
|
55
|
+
users.each_with_object({}) do |user, result|
|
56
|
+
key = OpenSSL::PKey::RSA.new(user.public_key)
|
57
|
+
type = key.ssh_type
|
58
|
+
data = [key.to_blob].pack('m0')
|
59
|
+
result[user.id] = "#{type} #{data}"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
def users
|
66
|
+
User.all
|
67
|
+
end
|
68
|
+
|
69
|
+
def all
|
70
|
+
where(type: :env).map { |s| unlock(s).unlocked_value }
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'data_mapper'
|
2
|
+
|
3
|
+
module Encipher
|
4
|
+
# An encipher secret
|
5
|
+
class Secret
|
6
|
+
include DataMapper::Resource
|
7
|
+
|
8
|
+
attr_accessor :unlocked_value
|
9
|
+
attr_accessor :locked
|
10
|
+
|
11
|
+
belongs_to :user
|
12
|
+
|
13
|
+
property :id, Serial
|
14
|
+
property :value, Text
|
15
|
+
|
16
|
+
def value=(v)
|
17
|
+
fail 'Secret must be unlocked to set the value' if locked
|
18
|
+
|
19
|
+
super v
|
20
|
+
end
|
21
|
+
|
22
|
+
def unlocked_value=(value)
|
23
|
+
@unlocked_value = value
|
24
|
+
@unlocked = true
|
25
|
+
end
|
26
|
+
|
27
|
+
def save
|
28
|
+
fail 'Cannot save an unlocked secret' unless locked
|
29
|
+
super
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# Encipher
|
2
|
+
module Encipher
|
3
|
+
# Handles key loading, encryption, and decryption
|
4
|
+
class Security
|
5
|
+
|
6
|
+
# Loads the private key
|
7
|
+
def initialize(private_key)
|
8
|
+
@private_key = Net::SSH::KeyFactory.load_private_key(private_key)
|
9
|
+
end
|
10
|
+
|
11
|
+
# Returns the pem public key associated with this security object
|
12
|
+
def public_key
|
13
|
+
require_key
|
14
|
+
|
15
|
+
@private_key.public_key.to_pem
|
16
|
+
end
|
17
|
+
|
18
|
+
# Encrypts a string of text with the given public key
|
19
|
+
def encrypt(string, public_key = nil)
|
20
|
+
require_key unless public_key
|
21
|
+
|
22
|
+
key = OpenSSL::PKey::RSA.new(public_key || @private_key.public_key.to_pem)
|
23
|
+
|
24
|
+
Base64.encode64(key.public_encrypt(string))
|
25
|
+
end
|
26
|
+
|
27
|
+
# Decrypts a string of text with the current private key
|
28
|
+
def decrypt(string)
|
29
|
+
require_key
|
30
|
+
|
31
|
+
@private_key.private_decrypt(Base64.decode64(string))
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
# Raises an error if no private key has been loaded
|
37
|
+
def require_key
|
38
|
+
fail 'No private key loaded' unless @private_key
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
require 'pry'
|
2
|
+
require 'data_mapper'
|
3
|
+
require 'encipher/security'
|
4
|
+
require 'encipher/models/user'
|
5
|
+
require 'encipher/models/secret'
|
6
|
+
require 'net/ssh'
|
7
|
+
require 'base64'
|
8
|
+
require 'deep_merge'
|
9
|
+
|
10
|
+
module Encipher
|
11
|
+
# The main encipher secrets storage entrypoint
|
12
|
+
class Vault
|
13
|
+
# Initalizes the database and loads the keys
|
14
|
+
def initialize(secret_key)
|
15
|
+
@secret_key = secret_key
|
16
|
+
initialize_database
|
17
|
+
end
|
18
|
+
|
19
|
+
# locks a secrets contents
|
20
|
+
def lock(secret)
|
21
|
+
secret.value = security.encrypt(secret.unlocked_value.to_yaml, secret.user.public_key)
|
22
|
+
secret.locked = true
|
23
|
+
secret
|
24
|
+
end
|
25
|
+
|
26
|
+
# unlocks a secrets contents
|
27
|
+
def unlock(secret)
|
28
|
+
secret.locked = false
|
29
|
+
secret.unlocked_value = YAML.load(security.decrypt(secret.value))
|
30
|
+
secret
|
31
|
+
end
|
32
|
+
|
33
|
+
def where(options)
|
34
|
+
require_user
|
35
|
+
|
36
|
+
[].tap do |r|
|
37
|
+
current_user.secrets.each do |secret|
|
38
|
+
contents = unlock(secret).unlocked_value
|
39
|
+
r << secret if compare(contents, { env: Encipher.env }.deep_merge(options))
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# checks if the keys/values in options are included in ojbect
|
45
|
+
def compare(object, options)
|
46
|
+
options.keys.each do |key|
|
47
|
+
return false unless object[key] == options[key]
|
48
|
+
end
|
49
|
+
|
50
|
+
true
|
51
|
+
end
|
52
|
+
|
53
|
+
# Enroll a new user's key or the current users
|
54
|
+
def enroll(key = nil, path: nil)
|
55
|
+
fail 'Must specify key OR path, not both' unless (!!key ^ !!path) || !(key || path)
|
56
|
+
|
57
|
+
if path
|
58
|
+
fail 'Bad key path' unless File.exist? File.expand_path(path)
|
59
|
+
data = File.read(File.expand_path path)
|
60
|
+
key = Net::SSH::KeyFactory.load_data_public_key(data).to_pem
|
61
|
+
elsif key
|
62
|
+
key = Net::SSH::KeyFactory.load_data_public_key(key).to_pem
|
63
|
+
else
|
64
|
+
key = security.public_key
|
65
|
+
end
|
66
|
+
|
67
|
+
if user_exists? key
|
68
|
+
puts 'Key already enrolled'
|
69
|
+
else
|
70
|
+
User.create(public_key: key)
|
71
|
+
puts 'Key was successfully enrolled'
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# Checks if a user exists by public key
|
76
|
+
def user_exists?(public_key)
|
77
|
+
User.all(public_key: public_key).count > 0
|
78
|
+
end
|
79
|
+
|
80
|
+
# Revoke a user by id
|
81
|
+
def revoke(user_id)
|
82
|
+
require_user
|
83
|
+
|
84
|
+
user = User.find(user_id)
|
85
|
+
user.secrets.each do |secret|
|
86
|
+
secret.destroy
|
87
|
+
end
|
88
|
+
|
89
|
+
user.destroy
|
90
|
+
end
|
91
|
+
|
92
|
+
protected
|
93
|
+
|
94
|
+
# returns the memoized security object
|
95
|
+
def security
|
96
|
+
return @security if defined? @security
|
97
|
+
@security = Security.new(@secret_key)
|
98
|
+
end
|
99
|
+
|
100
|
+
# Finalizes datamapper and initializes the sqlite database
|
101
|
+
def initialize_database(database_path = './secrets.db')
|
102
|
+
DataMapper.finalize
|
103
|
+
|
104
|
+
database_path = File.expand_path database_path
|
105
|
+
|
106
|
+
@dm = DataMapper.setup(:default, "sqlite:///#{database_path}")
|
107
|
+
|
108
|
+
return if File.exist? database_path
|
109
|
+
|
110
|
+
SQLite3::Database.new(database_path)
|
111
|
+
DataMapper.auto_migrate!
|
112
|
+
end
|
113
|
+
|
114
|
+
# Gets the current user object
|
115
|
+
def current_user
|
116
|
+
User.all(public_key: security.public_key).first
|
117
|
+
end
|
118
|
+
|
119
|
+
def require_user
|
120
|
+
fail 'Current user must be enrolled' unless current_user
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
data/secrets.db
ADDED
Binary file
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Encipher::Cli::Base do
|
4
|
+
let(:key_path) { 'keypath' }
|
5
|
+
let(:config) { double(key_path: 'keypath') }
|
6
|
+
|
7
|
+
context do
|
8
|
+
before :each do
|
9
|
+
allow(subject).to receive(:config) { config }
|
10
|
+
end
|
11
|
+
|
12
|
+
describe '.environment' do
|
13
|
+
let(:environment) { double }
|
14
|
+
|
15
|
+
it 'memoizes the environment object' do
|
16
|
+
subject.instance_variable_set(:@environment, environment)
|
17
|
+
expect(Encipher::Environment).to_not receive(:new)
|
18
|
+
expect(subject.send(:environment)).to be environment
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'creates a new environment object if not set' do
|
22
|
+
expect(Encipher::Environment).to receive(:new).with(key_path) { environment }
|
23
|
+
expect(subject.send(:environment)).to be(environment)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe '.vault' do
|
28
|
+
let(:vault) { double }
|
29
|
+
|
30
|
+
it 'memoizes the vault object' do
|
31
|
+
subject.instance_variable_set(:@vault, vault)
|
32
|
+
expect(Encipher::Vault).to_not receive(:new)
|
33
|
+
expect(subject.send(:vault)).to be vault
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'creates a new vault object if not set' do
|
37
|
+
expect(Encipher::Vault).to receive(:new).with(key_path) { vault }
|
38
|
+
expect(subject.send(:vault)).to be(vault)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
describe '.config' do
|
44
|
+
let (:yaml_hash) { double }
|
45
|
+
let (:config) { double }
|
46
|
+
|
47
|
+
before :each do
|
48
|
+
allow(OpenStruct).to receive(:new) { config }
|
49
|
+
allow(YAML).to receive(:load) { yaml_hash }
|
50
|
+
allow(File).to receive(:read)
|
51
|
+
allow(File).to receive(:exist?) { true }
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'memoizes the config object' do
|
55
|
+
subject.instance_variable_set(:@config, config)
|
56
|
+
expect(OpenStruct).to_not receive(:new)
|
57
|
+
expect(subject.send(:config)).to be config
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'loads the config from yaml' do
|
61
|
+
expect(YAML).to receive(:load)
|
62
|
+
subject.send(:config)
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'creates a new config object if not set' do
|
66
|
+
expect(OpenStruct).to receive(:new).with(yaml_hash) { config }
|
67
|
+
expect(subject.send(:config)).to be(config)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'pry-byebug'
|
2
|
+
require 'encipher'
|
3
|
+
require 'encipher/cli/cli'
|
4
|
+
|
5
|
+
# This file was generated by the `rails generate rspec:install` command. Conventionally, all
|
6
|
+
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
|
7
|
+
# The generated `.rspec` file contains `--require spec_helper` which will cause this
|
8
|
+
# file to always be loaded, without a need to explicitly require it in any files.
|
9
|
+
#
|
10
|
+
# Given that it is always loaded, you are encouraged to keep this file as
|
11
|
+
# light-weight as possible. Requiring heavyweight dependencies from this file
|
12
|
+
# will add to the boot time of your test suite on EVERY test run, even for an
|
13
|
+
# individual file that may not need all of that loaded. Instead, make a
|
14
|
+
# separate helper file that requires this one and then use it only in the specs
|
15
|
+
# that actually need it.
|
16
|
+
#
|
17
|
+
# The `.rspec` file also contains a few flags that are not defaults but that
|
18
|
+
# users commonly want.
|
19
|
+
#
|
20
|
+
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
21
|
+
RSpec.configure do |config|
|
22
|
+
# The settings below are suggested to provide a good initial experience
|
23
|
+
# with RSpec, but feel free to customize to your heart's content.
|
24
|
+
# These two settings work together to allow you to limit a spec run
|
25
|
+
# to individual examples or groups you care about by tagging them with
|
26
|
+
# `:focus` metadata. When nothing is tagged with `:focus`, all examples
|
27
|
+
# get run.
|
28
|
+
config.filter_run :focus
|
29
|
+
config.run_all_when_everything_filtered = true
|
30
|
+
|
31
|
+
# Many RSpec users commonly either run the entire suite or an individual
|
32
|
+
# file, and it's useful to allow more verbose output when running an
|
33
|
+
# individual spec file.
|
34
|
+
if config.files_to_run.one?
|
35
|
+
# Use the documentation formatter for detailed output,
|
36
|
+
# unless a formatter has already been configured
|
37
|
+
# (e.g. via a command-line flag).
|
38
|
+
config.default_formatter = 'doc'
|
39
|
+
end
|
40
|
+
|
41
|
+
# Print the 10 slowest examples and example groups at the
|
42
|
+
# end of the spec run, to help surface which specs are running
|
43
|
+
# particularly slow.
|
44
|
+
config.profile_examples = 10
|
45
|
+
|
46
|
+
# Run specs in random order to surface order dependencies. If you find an
|
47
|
+
# order dependency and want to debug it, you can fix the order by providing
|
48
|
+
# the seed, which is printed after each run.
|
49
|
+
# --seed 1234
|
50
|
+
config.order = :random
|
51
|
+
|
52
|
+
# Seed global randomization in this process using the `--seed` CLI option.
|
53
|
+
# Setting this allows you to use `--seed` to deterministically reproduce
|
54
|
+
# test failures related to randomization by passing the same `--seed` value
|
55
|
+
# as the one that triggered the failure.
|
56
|
+
Kernel.srand config.seed
|
57
|
+
|
58
|
+
# rspec-expectations config goes here. You can use an alternate
|
59
|
+
# assertion/expectation library such as wrong or the stdlib/minitest
|
60
|
+
# assertions if you prefer.
|
61
|
+
config.expect_with :rspec do |expectations|
|
62
|
+
# Enable only the newer, non-monkey-patching expect syntax.
|
63
|
+
# For more details, see:
|
64
|
+
# - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax
|
65
|
+
expectations.syntax = :expect
|
66
|
+
end
|
67
|
+
|
68
|
+
config.color = true
|
69
|
+
|
70
|
+
# rspec-mocks config goes here. You can use an alternate test double
|
71
|
+
# library (such as bogus or mocha) by changing the `mock_with` option here.
|
72
|
+
config.mock_with :rspec do |mocks|
|
73
|
+
# Enable only the newer, non-monkey-patching expect syntax.
|
74
|
+
# For more details, see:
|
75
|
+
# - http://teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
|
76
|
+
mocks.syntax = :expect
|
77
|
+
|
78
|
+
# Prevents you from mocking or stubbing a method that does not exist on
|
79
|
+
# a real object. This is generally recommended.
|
80
|
+
mocks.verify_partial_doubles = true
|
81
|
+
end
|
82
|
+
end
|
metadata
ADDED
@@ -0,0 +1,296 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: encipher
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1a
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Joey Lorich
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-01-27 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.6'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.6'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ~>
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 10.3.2
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ~>
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 10.3.2
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: pry-byebug
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ~>
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 1.3.3
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 1.3.3
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 3.0.0
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ~>
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 3.0.0
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: clint_eastwood
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ~>
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 0.0.1
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ~>
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 0.0.1
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: thor
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ~>
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 0.19.1
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ~>
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: 0.19.1
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: highline
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ~>
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: 1.6.21
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ~>
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: 1.6.21
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: deep_merge
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ~>
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: 1.0.1
|
118
|
+
type: :runtime
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ~>
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: 1.0.1
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: net-ssh
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ~>
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: 2.9.1
|
132
|
+
type: :runtime
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ~>
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: 2.9.1
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: sqlite3
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - ~>
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: 1.3.9
|
146
|
+
type: :runtime
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - ~>
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: 1.3.9
|
153
|
+
- !ruby/object:Gem::Dependency
|
154
|
+
name: data_mapper
|
155
|
+
requirement: !ruby/object:Gem::Requirement
|
156
|
+
requirements:
|
157
|
+
- - ~>
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: 1.2.0
|
160
|
+
type: :runtime
|
161
|
+
prerelease: false
|
162
|
+
version_requirements: !ruby/object:Gem::Requirement
|
163
|
+
requirements:
|
164
|
+
- - ~>
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: 1.2.0
|
167
|
+
- !ruby/object:Gem::Dependency
|
168
|
+
name: dm-sqlite-adapter
|
169
|
+
requirement: !ruby/object:Gem::Requirement
|
170
|
+
requirements:
|
171
|
+
- - ~>
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: 1.2.0
|
174
|
+
type: :runtime
|
175
|
+
prerelease: false
|
176
|
+
version_requirements: !ruby/object:Gem::Requirement
|
177
|
+
requirements:
|
178
|
+
- - ~>
|
179
|
+
- !ruby/object:Gem::Version
|
180
|
+
version: 1.2.0
|
181
|
+
- !ruby/object:Gem::Dependency
|
182
|
+
name: dot_configure
|
183
|
+
requirement: !ruby/object:Gem::Requirement
|
184
|
+
requirements:
|
185
|
+
- - ~>
|
186
|
+
- !ruby/object:Gem::Version
|
187
|
+
version: 0.0.1
|
188
|
+
type: :runtime
|
189
|
+
prerelease: false
|
190
|
+
version_requirements: !ruby/object:Gem::Requirement
|
191
|
+
requirements:
|
192
|
+
- - ~>
|
193
|
+
- !ruby/object:Gem::Version
|
194
|
+
version: 0.0.1
|
195
|
+
- !ruby/object:Gem::Dependency
|
196
|
+
name: exedit
|
197
|
+
requirement: !ruby/object:Gem::Requirement
|
198
|
+
requirements:
|
199
|
+
- - ~>
|
200
|
+
- !ruby/object:Gem::Version
|
201
|
+
version: 0.0.2
|
202
|
+
type: :runtime
|
203
|
+
prerelease: false
|
204
|
+
version_requirements: !ruby/object:Gem::Requirement
|
205
|
+
requirements:
|
206
|
+
- - ~>
|
207
|
+
- !ruby/object:Gem::Version
|
208
|
+
version: 0.0.2
|
209
|
+
- !ruby/object:Gem::Dependency
|
210
|
+
name: awesome_print
|
211
|
+
requirement: !ruby/object:Gem::Requirement
|
212
|
+
requirements:
|
213
|
+
- - ~>
|
214
|
+
- !ruby/object:Gem::Version
|
215
|
+
version: 1.2.0
|
216
|
+
type: :runtime
|
217
|
+
prerelease: false
|
218
|
+
version_requirements: !ruby/object:Gem::Requirement
|
219
|
+
requirements:
|
220
|
+
- - ~>
|
221
|
+
- !ruby/object:Gem::Version
|
222
|
+
version: 1.2.0
|
223
|
+
- !ruby/object:Gem::Dependency
|
224
|
+
name: recursive-open-struct
|
225
|
+
requirement: !ruby/object:Gem::Requirement
|
226
|
+
requirements:
|
227
|
+
- - ~>
|
228
|
+
- !ruby/object:Gem::Version
|
229
|
+
version: 0.5.0
|
230
|
+
type: :runtime
|
231
|
+
prerelease: false
|
232
|
+
version_requirements: !ruby/object:Gem::Requirement
|
233
|
+
requirements:
|
234
|
+
- - ~>
|
235
|
+
- !ruby/object:Gem::Version
|
236
|
+
version: 0.5.0
|
237
|
+
description: Secure secrets management description!
|
238
|
+
email:
|
239
|
+
- joseph@lorich.me
|
240
|
+
executables:
|
241
|
+
- encipher
|
242
|
+
extensions: []
|
243
|
+
extra_rdoc_files: []
|
244
|
+
files:
|
245
|
+
- .env
|
246
|
+
- .gitignore
|
247
|
+
- .travis.yml
|
248
|
+
- Gemfile
|
249
|
+
- LICENSE.txt
|
250
|
+
- README.md
|
251
|
+
- Rakefile
|
252
|
+
- bin/encipher
|
253
|
+
- config/rubocop.yml
|
254
|
+
- encipher.gemspec
|
255
|
+
- lib/encipher.rb
|
256
|
+
- lib/encipher/cli/base.rb
|
257
|
+
- lib/encipher/cli/cli.rb
|
258
|
+
- lib/encipher/cli/convert.rb
|
259
|
+
- lib/encipher/dotenv.rb
|
260
|
+
- lib/encipher/encipher.rb
|
261
|
+
- lib/encipher/environment.rb
|
262
|
+
- lib/encipher/models/secret.rb
|
263
|
+
- lib/encipher/models/user.rb
|
264
|
+
- lib/encipher/security.rb
|
265
|
+
- lib/encipher/vault.rb
|
266
|
+
- lib/encipher/version.rb
|
267
|
+
- secrets.db
|
268
|
+
- spec/enchiper/cli/base_spec.rb
|
269
|
+
- spec/spec_helper.rb
|
270
|
+
homepage: ''
|
271
|
+
licenses:
|
272
|
+
- MIT
|
273
|
+
metadata: {}
|
274
|
+
post_install_message:
|
275
|
+
rdoc_options: []
|
276
|
+
require_paths:
|
277
|
+
- lib
|
278
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
279
|
+
requirements:
|
280
|
+
- - '>='
|
281
|
+
- !ruby/object:Gem::Version
|
282
|
+
version: '0'
|
283
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
284
|
+
requirements:
|
285
|
+
- - '>'
|
286
|
+
- !ruby/object:Gem::Version
|
287
|
+
version: 1.3.1
|
288
|
+
requirements: []
|
289
|
+
rubyforge_project:
|
290
|
+
rubygems_version: 2.0.14
|
291
|
+
signing_key:
|
292
|
+
specification_version: 4
|
293
|
+
summary: Secure secrets management
|
294
|
+
test_files:
|
295
|
+
- spec/enchiper/cli/base_spec.rb
|
296
|
+
- spec/spec_helper.rb
|