envault 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: af2de945195b941624c2f5e2a992f3d8eea9eb80
4
+ data.tar.gz: 22faf369eead70da1d5e4bddf896b13df3318030
5
+ SHA512:
6
+ metadata.gz: dcd47c0296c6c41ba87c8a4e4d1b51bd3da4fa4be65dd5c0f17d34738b26d9472a27b586a33656c3402d6225e916376d133d6ebddc7be8e61ca7adf1e2ab1692
7
+ data.tar.gz: b186a443ff600835a0e04460c682ab4ff35d8602cc4b155eaa2bce7a961d72fa08dc4052244887db39029913d4088292ffa42c430db9bb55408ef894d0aeeccc
data/.document ADDED
@@ -0,0 +1,3 @@
1
+ -
2
+ ChangeLog.md
3
+ LICENSE.txt
data/.gitignore ADDED
@@ -0,0 +1,8 @@
1
+ Gemfile.lock
2
+ doc/
3
+ pkg/
4
+ **/*.gem
5
+ .yardoc/
6
+ .bundle/
7
+ vendor/bundle/
8
+ .tags
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --colour --format documentation
data/.travis.yml ADDED
@@ -0,0 +1,16 @@
1
+ language: ruby
2
+
3
+ rvm:
4
+ - 2.1.*
5
+ - 2.2.*
6
+ - 2.3.1
7
+
8
+ gemfile:
9
+ - Gemfile
10
+
11
+ os:
12
+ - linux
13
+
14
+ before_install: gem update bundler
15
+
16
+ script: 'bundle exec rake spec'
data/.yardopts ADDED
@@ -0,0 +1 @@
1
+ --markup markdown --title "envault Documentation" --protected
data/ChangeLog.md ADDED
@@ -0,0 +1,4 @@
1
+ ### 0.1.0 / 2016-08-31
2
+
3
+ * Initial release:
4
+
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ group :development do
6
+ gem 'kramdown'
7
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2016 toyama0919
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,180 @@
1
+ # envault [![Build Status](https://secure.travis-ci.org/toyama0919/envault.png?branch=master)](http://travis-ci.org/toyama0919/envault)
2
+
3
+ Encrypt secret information environment variables by yaml.
4
+
5
+ ## Settings(Environment Variables)
6
+ ```
7
+ export ENVAULT_PASSPHRASE=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
8
+ export ENVAULT_SIGN_PASSPHRASE=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
9
+ export ENVAULT_SALT=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
10
+ ```
11
+
12
+ ## Settings(yaml file)
13
+ ```
14
+ development:
15
+ passphrase: ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ
16
+ sign_passphrase: ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ
17
+ salt: ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ
18
+ prefix: ENVAULT_
19
+
20
+ staging:
21
+ passphrase: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
22
+ sign_passphrase: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
23
+ salt: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
24
+ prefix: ENVAULT_
25
+
26
+ production:
27
+ passphrase: YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY
28
+ sign_passphrase: YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY
29
+ salt: YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY
30
+ prefix: ENVAULT_
31
+ ```
32
+
33
+ ## Encrypt and Decrypt
34
+ ```bash
35
+ $ cat .env
36
+ USERNAME_A: hogehoge
37
+ USERNAME_B: fugafuga
38
+ USERNAME_C: mogomogo
39
+ PASSWORD_A: hogehoge
40
+ PASSWORD_B: fugafuga
41
+ PASSWORD_C: mogomogo
42
+ API_KEY_A: hogehoge
43
+ API_KEY_B: fugafuga
44
+ API_KEY_C: mogomogo
45
+
46
+ ## encrypt file
47
+ $ envault -e -s .env -c envault.yml --profile staging -k '^PASSWORD_.*' '^API_KEY_.*' > .env.encrypt
48
+ $ cat .env.encrypt
49
+ USERNAME_A: "hogehoge"
50
+ USERNAME_B: "fugafuga"
51
+ USERNAME_C: "mogomogo"
52
+ ENVAULT_PASSWORD_A: "VmI4TkcwYXFRdnp3cTNINFo5NHZNWWtUakd4WE9iWDhJdFIzVnQydXlMaz0tLU5CS2JONW1FalorMGxsOGxUYmpXUFE9PQ==--3e301c251f5a7cf0e6280daa3bc14cc04c2cbff492758028c9e5fd6ddc72660e"
53
+ ENVAULT_PASSWORD_B: "QzI1eFZnampSZkk3QWxEYkZjemNlMVpmWWVEVFluZjhJV01zS3JKNUlvST0tLUNvWDdNWVFGMUMwVGEvaTNFMkJVU2c9PQ==--d58c39f5e71b382f2d2778e8c02c58339ed330e0dc31067ed6544fcb94397700"
54
+ ENVAULT_PASSWORD_C: "eGo0S3pLRWV0OFRrdVRzTmwvZlR3VkN6a2xjeHpvcHV0ZlZMenNOUm1Wbz0tLS80WjFuRzQrQ29uSU5SbDBSOGUyRlE9PQ==--7c2342c9533b70af50be5cf1dd12aa66f595263ea4c8aa347b185a7a8e57fb3c"
55
+ ENVAULT_API_KEY_A: "QThLSGF4VXNST3ZXL0VTVURzMlQ3aUE2aXppTlc5aUxUWk9Xa0hXS25NYz0tLTAxWlI0OU0zdnZXUG1MdmtYY2FZK0E9PQ==--fff50bafac593d6c50da369f1e040e0f6db8623299078ccda029bbeed12a93c7"
56
+ ENVAULT_API_KEY_B: "cWdFS21HdnArNlBzcFhremhFNTJzdzhtYkNwWUIrb2dzekFsbzZxQjRsQT0tLWZUZTdpYW1Bc2xqRXcvMjB4eDRNc1E9PQ==--edb6d0bace9f1cd4c9eeef0a9289d43fd6724625e601aa46e9ebb12f6405efb6"
57
+ ENVAULT_API_KEY_C: "YllDcDhYUTJGZWhTRjBaQTU4L3RlZitzYVN3OTV6OXhSbkZHbFBWaWF3cz0tLVo1MGFZVkNWQ3g2UXdwRlBFaW43MWc9PQ==--fd0642530754f235856f9ebba252bb34156666498433e05c2ce29573aad6ec69"
58
+
59
+ ## decrypt file
60
+ $ envault -d -s .env.encrypt -c envault.yml --profile staging
61
+ USERNAME_A: "hogehoge"
62
+ USERNAME_B: "fugafuga"
63
+ USERNAME_C: "mogomogo"
64
+ PASSWORD_A: "hogehoge"
65
+ PASSWORD_B: "fugafuga"
66
+ PASSWORD_C: "mogomogo"
67
+ API_KEY_A: "hogehoge"
68
+ API_KEY_B: "fugafuga"
69
+ API_KEY_C: "mogomogo"
70
+
71
+ ## if use other profile, Error
72
+ $ envault -d -s .env.encrypt -c envault.yml --profile production 1 ↵
73
+ /Users/toyama-h/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/activesupport-4.2.5/lib/active_support/message_verifier.rb:49:in `verify': ActiveSupport::MessageVerifier::InvalidSignature (ActiveSupport::MessageVerifier::InvalidSignature)
74
+ from /Users/toyama-h/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/activesupport-4.2.5/lib/active_support/message_encryptor.rb:64:in `decrypt_and_verify'
75
+ from /Users/toyama-h/Dropbox/github/envault/lib/envault/core.rb:51:in `block in decrypt_process'
76
+ from /Users/toyama-h/Dropbox/github/envault/lib/envault/core.rb:49:in `each'
77
+ from /Users/toyama-h/Dropbox/github/envault/lib/envault/core.rb:49:in `map'
78
+ from /Users/toyama-h/Dropbox/github/envault/lib/envault/core.rb:49:in `decrypt_process'
79
+ from /Users/toyama-h/Dropbox/github/envault/lib/envault/core.rb:44:in `decrypt_yaml'
80
+ from /Users/toyama-h/Dropbox/github/envault/lib/envault/cli.rb:74:in `block in decrypt_file'
81
+ from /Users/toyama-h/Dropbox/github/envault/lib/envault/cli.rb:73:in `each'
82
+ from /Users/toyama-h/Dropbox/github/envault/lib/envault/cli.rb:73:in `decrypt_file'
83
+ from /Users/toyama-h/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/thor-0.19.1/lib/thor/command.rb:27:in `run'
84
+ from /Users/toyama-h/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/thor-0.19.1/lib/thor/invocation.rb:126:in `invoke_command'
85
+ from /Users/toyama-h/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/thor-0.19.1/lib/thor.rb:359:in `dispatch'
86
+ from /Users/toyama-h/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/thor-0.19.1/lib/thor/base.rb:440:in `start'
87
+ from /Users/toyama-h/Dropbox/github/envault/bin/envault:6:in `<top (required)>'
88
+ from /Users/toyama-h/bin/envault:17:in `load'
89
+ from /Users/toyama-h/bin/envault:17:in `<main>'
90
+ ```
91
+
92
+ ## reencrypt(config)
93
+ ```bash
94
+ $ cat .envault.test
95
+ old_staging:
96
+ passphrase: ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ
97
+ sign_passphrase: ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ
98
+ salt: ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ
99
+ prefix: OLD_ENVAULT_
100
+
101
+ staging:
102
+ passphrase: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
103
+ sign_passphrase: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
104
+ salt: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
105
+ prefix: ENVAULT_
106
+
107
+ $ cat .env.encrypt
108
+ OLD_ENVAULT_A: "aaaaaaaaaaaaaa"
109
+ OLD_ENVAULT_B: "bbbbbbbbbbbbbbb"
110
+ C: "hoge"
111
+
112
+ $ envault reencrypt_file -s .env.encrypt -c ~/.envault --from_profile old_staging --to_profile staging --overwrite
113
+
114
+ $ cat .env.encrypt
115
+ ENVAULT_A: "ccccccccccccccc"
116
+ ENVAULT_B: "ddddddddddddddd"
117
+ C: "hoge"
118
+
119
+ ```
120
+
121
+ ## Load AND command(Environment Variables)
122
+ ```bash
123
+ $ envault load -s .env.encrypt --command 'echo $PASSWORD_A'
124
+ hogehoge
125
+ ```
126
+
127
+ ## Load Application(Environment Variables)
128
+ ```bash
129
+ require 'envault'
130
+ Envault.load('.env.encrypt')
131
+ p ENV['PASSWORD_A']
132
+ #=> hogehoge
133
+ ```
134
+
135
+ ## Load Application(Profile)
136
+ ```bash
137
+ require 'envault'
138
+ Envault.load_with_profile('.env.encrypt', config: '.envault', profile: 'staging')
139
+ p ENV['PASSWORD_B']
140
+ #=> fugafuga
141
+ ```
142
+
143
+ ## Installation
144
+
145
+ Add this line to your application's Gemfile:
146
+
147
+ gem 'envault'
148
+
149
+ And then execute:
150
+
151
+ $ bundle
152
+
153
+ Or install it yourself as:
154
+
155
+ $ gem install envault
156
+
157
+ ## Synopsis
158
+
159
+ $ envault
160
+
161
+ ## Contributing
162
+
163
+ 1. Fork it
164
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
165
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
166
+ 4. Push to the branch (`git push origin my-new-feature`)
167
+ 5. Create new [Pull Request](../../pull/new/master)
168
+
169
+ ## Information
170
+
171
+ * [Homepage](https://github.com/toyama0919/envault)
172
+ * [Issues](https://github.com/toyama0919/envault/issues)
173
+ * [Documentation](http://rubydoc.info/gems/envault/frames)
174
+ * [Email](mailto:toyama0919@gmail.com)
175
+
176
+ ## Copyright
177
+
178
+ Copyright (c) 2016 toyama0919
179
+
180
+ See [LICENSE.txt](../LICENSE.txt) for details.
data/Rakefile ADDED
@@ -0,0 +1,25 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+
5
+ begin
6
+ require 'bundler/setup'
7
+ rescue LoadError => e
8
+ abort e.message
9
+ end
10
+
11
+ require 'rake'
12
+
13
+
14
+ require 'rubygems/tasks'
15
+ Gem::Tasks.new
16
+
17
+ require 'rspec/core/rake_task'
18
+ RSpec::Core::RakeTask.new
19
+
20
+ task :test => :spec
21
+ task :default => :spec
22
+
23
+ require 'yard'
24
+ YARD::Rake::YardocTask.new
25
+ task :doc => :yard
data/bin/envault ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require 'envault'
5
+
6
+ Envault::CLI.start
data/envault.gemspec ADDED
@@ -0,0 +1,33 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require File.expand_path('../lib/envault/version', __FILE__)
4
+
5
+ Gem::Specification.new do |gem|
6
+ gem.name = "envault"
7
+ gem.version = Envault::VERSION
8
+ gem.summary = %q{Encrypt secret information environment variables by yaml.}
9
+ gem.description = %q{Encrypt secret information environment variables by yaml.}
10
+ gem.license = "MIT"
11
+ gem.authors = ["toyama0919"]
12
+ gem.email = "toyama0919@gmail.com"
13
+ gem.homepage = "https://github.com/toyama0919/envault"
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ['lib']
19
+
20
+ gem.required_ruby_version = '>= 2.1'
21
+
22
+ gem.add_dependency 'thor'
23
+ gem.add_dependency 'dotenv'
24
+ gem.add_dependency 'activesupport', ">=4", "<5"
25
+
26
+ gem.add_development_dependency 'bundler'
27
+ gem.add_development_dependency 'pry'
28
+ gem.add_development_dependency 'rake'
29
+ gem.add_development_dependency 'rspec', '~> 3.0'
30
+ gem.add_development_dependency 'rubocop', '~> 0.24.1'
31
+ gem.add_development_dependency 'rubygems-tasks', '~> 0.2'
32
+ gem.add_development_dependency 'yard', '~> 0.8'
33
+ end
data/lib/envault.rb ADDED
@@ -0,0 +1,22 @@
1
+ require 'envault/version'
2
+ require 'envault/constants'
3
+ require 'envault/core'
4
+ require 'envault/cli'
5
+ require 'envault/environment'
6
+ require 'envault/formatter'
7
+
8
+ module Envault
9
+ def self.load(*source_files)
10
+ source_files = ['.env'] if source_files.empty?
11
+ params = ['load', '--sources', source_files]
12
+ Envault::CLI.start(params)
13
+ end
14
+
15
+ def self.load_with_profile(*source_files, config:, profile:)
16
+ source_files = ['.env'] if source_files.empty?
17
+ params = ['load', '--sources', source_files]
18
+ params.concat(['-c', config]) if config
19
+ params.concat(['--profile', profile]) if profile
20
+ Envault::CLI.start(params)
21
+ end
22
+ end
@@ -0,0 +1,126 @@
1
+ require 'thor'
2
+ require 'erb'
3
+ require 'yaml'
4
+
5
+ module Envault
6
+ class CLI < Thor
7
+ map '-e' => :encrypt_file
8
+ map '-d' => :decrypt_file
9
+ map '-r' => :reencrypt_file
10
+ map '-l' => :load
11
+
12
+ class_option :config, aliases: '-c', type: :string ,desc: 'config'
13
+ class_option :profile, aliases: '-p', type: :string, default: 'default',desc: 'profile'
14
+ class_option :debug, aliases: '--debug', type: :boolean, default: false, desc: 'debug'
15
+ class_option :prefix, type: :string, default: nil, desc: 'prefix'
16
+ def initialize(args = [], options = {}, config = {})
17
+ super(args, options, config)
18
+ @class_options = config[:shell].base.options
19
+ current_command = config[:current_command].name
20
+ unless SKIP_INITIALIZE_COMMANDS.include?(current_command)
21
+ @core = Core.new(
22
+ config: @class_options[:config],
23
+ profile: @class_options[:profile],
24
+ prefix: @class_options[:prefix],
25
+ debug: @class_options[:debug]
26
+ )
27
+ @logger = @core.logger
28
+ end
29
+ end
30
+
31
+ desc "encrypt", "encrypt string. exp: envault encrypt -s hoge"
32
+ option :source, aliases: '-s', type: :string, required: true, desc: 'source', banner: 'source'
33
+ def encrypt
34
+ puts @core.cryptor.encrypt_and_sign(options[:source])
35
+ end
36
+
37
+ desc "decrypt", "decrypt string. exp: envault decrypt -s hoge"
38
+ option :source, aliases: '-s', type: :string, required: true, desc: 'source'
39
+ def decrypt
40
+ puts @core.cryptor.decrypt_and_verify(options[:source])
41
+ end
42
+
43
+ desc "-r", "reencrypt file. exp: envault -r -s .env.encrypt -c ~/.envault --from_profile staging --to_profile production"
44
+ option :source, aliases: '-s', type: :string, required: true, desc: 'source'
45
+ option :from_profile, type: :string, required: true, desc: 'from_profile'
46
+ option :to_profile, type: :string, required: true, desc: 'to_profile'
47
+ option :overwrite, type: :boolean, default: false, desc: 'overwrite'
48
+ def reencrypt_file
49
+ yaml = YAML.load_file(options[:source])
50
+ from = Core.new(
51
+ config: @class_options[:config],
52
+ profile: options[:from_profile],
53
+ prefix: @class_options[:prefix],
54
+ debug: @class_options[:debug]
55
+ )
56
+ cipher_keys = from.get_cipher_keys(yaml).map{ |cipher_key| cipher_key.gsub(/^#{from.prefix}/, '') }
57
+ decrypted = from.decrypt_yaml(options[:source])
58
+ to = Core.new(
59
+ config: @class_options[:config],
60
+ profile: options[:to_profile],
61
+ prefix: @class_options[:prefix],
62
+ debug: @class_options[:debug]
63
+ )
64
+ output = to.encrypt_process(decrypted, cipher_keys)
65
+ if options[:overwrite]
66
+ Formatter.write_escape_yaml(options[:source], output)
67
+ else
68
+ puts Formatter.escape_yaml(output)
69
+ end
70
+ end
71
+
72
+ desc "-e", "encrypt file. exp. envault -e -s .env -k '^PASSWORD_.*' '^API_KEY_.*'"
73
+ option :source, aliases: '-s', type: :array, required: true, desc: 'secret'
74
+ option :keys, aliases: '-k', type: :array, required: false, desc: 'keys'
75
+ option :plain_text, aliases: '-t', type: :array, default: [], required: false, desc: 'plain'
76
+ option :output, aliases: '-o', type: :string, default: nil, desc: 'output'
77
+ def encrypt_file
78
+ result = {}
79
+ options[:plain_text].each do |plain_text_path|
80
+ result = result.merge(YAML.load(ERB.new(File.read(plain_text_path)).result))
81
+ end
82
+ options[:source].each do |secret_yaml_path|
83
+ result = result.merge(@core.encrypt_yaml(secret_yaml_path, options[:keys]))
84
+ end
85
+ if options[:output]
86
+ Formatter.write_escape_yaml(options[:output], result)
87
+ else
88
+ puts Formatter.escape_yaml(result)
89
+ end
90
+ end
91
+
92
+ desc "-d", "decrypt file. exp: envault -d -s .env"
93
+ option :source, aliases: '-s', type: :array, required: true, desc: 'source'
94
+ option :plain_text, aliases: '-t', type: :array, default: [], required: false, desc: 'plain_text'
95
+ option :output, aliases: '-o', type: :string, default: nil, desc: 'output'
96
+ def decrypt_file
97
+ result = {}
98
+ options[:plain_text].each do |plain_text_path|
99
+ result = result.merge(YAML.load(ERB.new(File.read(plain_text_path)).result))
100
+ end
101
+ options[:source].each do |encrypt_yaml_path|
102
+ result = result.merge(@core.decrypt_yaml(encrypt_yaml_path))
103
+ end
104
+ if options[:output]
105
+ Formatter.write_escape_yaml(options[:output], result)
106
+ else
107
+ puts Formatter.escape_yaml(result)
108
+ end
109
+ end
110
+
111
+ desc "-l", "load environment. exp: envault -l -s .env --command 'echo $myhostname'"
112
+ option :sources, aliases: '-s', type: :array, required: true, default: [DEFAULT_SOURCE_FILE], desc: 'source'
113
+ option :command, type: :string, desc: 'source'
114
+ def load
115
+ options[:sources].each do |source|
116
+ begin
117
+ @core.load(source)
118
+ rescue => e
119
+ raise "error => [#{source}]\n#{e.message}\n#{e.backtrace.join("\n")}"
120
+ end
121
+ end
122
+ @logger.debug(ENV)
123
+ exec(options[:command]) if options[:command]
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,7 @@
1
+ module Envault
2
+ DEFAULT_SOURCE_FILE = ".env"
3
+ DEFAULT_ENV_PREFIX = "ENVAULT_"
4
+ DEFAULT_CIPHER = "aes-256-cbc"
5
+ DEFAULT_DIGEST = "SHA256"
6
+ SKIP_INITIALIZE_COMMANDS = ["reencrypt_file"]
7
+ end
@@ -0,0 +1,118 @@
1
+ require 'dotenv'
2
+ require 'pp'
3
+ require 'logger'
4
+ require 'tempfile'
5
+ require 'active_support'
6
+
7
+ module Envault
8
+ class Core
9
+ attr_accessor :logger, :cryptor, :prefix
10
+
11
+ def initialize(config: nil, profile: nil, prefix: nil, debug: false)
12
+ @logger = Logger.new(STDOUT)
13
+ @logger.level = debug ? Logger::DEBUG : Logger::INFO
14
+ profile = get_profile(config, profile)
15
+ @cryptor = get_cryptor(profile[:passphrase] || '', profile[:sign_passphrase], profile[:salt] || '')
16
+ @prefix = prefix || profile[:prefix] || DEFAULT_ENV_PREFIX
17
+ end
18
+
19
+ def encrypt_yaml(path, keys = nil)
20
+ hash = YAML.load_file(path)
21
+ encrypt_process(hash, keys)
22
+ end
23
+
24
+ def encrypt_process(hash, keys = nil)
25
+ cipher_keys = get_cipher_keys(hash, keys)
26
+ encrypted = hash.map do |k, v|
27
+ if cipher_keys.include?(k)
28
+ [@prefix + k, @cryptor.encrypt_and_sign(v)]
29
+ else
30
+ [k, v]
31
+ end
32
+ end
33
+ Hash[encrypted]
34
+ end
35
+
36
+ def decrypt_yaml(path)
37
+ hash = YAML.load_file(path)
38
+ decrypt_process(hash)
39
+ end
40
+
41
+ def decrypt_process(hash)
42
+ cipher_keys = get_cipher_keys(hash)
43
+ decrypted = hash.map do |k, v|
44
+ if cipher_keys.include?(k)
45
+ [k.gsub(/^#{@prefix}/, ''), @cryptor.decrypt_and_verify(v)]
46
+ else
47
+ [k, v]
48
+ end
49
+ end
50
+ Hash[decrypted]
51
+ end
52
+
53
+ def get_cipher_keys(hash, keys = ["^#{@prefix}.*"])
54
+ all_keys = hash.keys
55
+ if keys
56
+ regexps = []
57
+ keys.each do |key|
58
+ regexps << Regexp.new(key)
59
+ end
60
+ results = regexps.map do |regexp|
61
+ all_keys.select do |key|
62
+ regexp =~ key
63
+ end
64
+ end
65
+ results.flatten
66
+ else
67
+ all_keys
68
+ end
69
+ end
70
+
71
+ def load(path = DEFAULT_SOURCE_FILE)
72
+ hash = decrypt_yaml(path)
73
+
74
+ Tempfile.create("dotenv-vault") do |f|
75
+ Formatter.write_escape_yaml(f.path, hash)
76
+ Dotenv.load(f.path)
77
+ end
78
+ end
79
+
80
+ private
81
+
82
+ def get_cryptor(passphrase, sign_passphrase, salt)
83
+ key = ActiveSupport::KeyGenerator.new(passphrase).generate_key(salt, 64)
84
+ signature_key = ActiveSupport::KeyGenerator.new(sign_passphrase).generate_key(salt, 64) if sign_passphrase
85
+
86
+ if signature_key
87
+ ActiveSupport::MessageEncryptor.new(key, signature_key, cipher: DEFAULT_CIPHER, digest: DEFAULT_DIGEST)
88
+ else
89
+ ActiveSupport::MessageEncryptor.new(key, cipher: DEFAULT_CIPHER, digest: DEFAULT_DIGEST)
90
+ end
91
+ end
92
+
93
+ def get_profile(config_path, profile_name)
94
+ return get_profile_form_env unless config_path
95
+ config = YAML.load_file(config_path)
96
+ return get_profile_form_env unless config
97
+ profile = config[profile_name]
98
+ unless profile
99
+ raise %Q{invalid profile [#{profile_name}].}
100
+ end
101
+ {
102
+ passphrase: profile['passphrase'],
103
+ sign_passphrase: profile['sign_passphrase'],
104
+ salt: profile['salt'],
105
+ prefix: profile['prefix']
106
+ }
107
+ end
108
+
109
+ def get_profile_form_env
110
+ {
111
+ passphrase: ENV['ENVAULT_PASSPHRASE'],
112
+ sign_passphrase: ENV['ENVAULT_SIGN_PASSPHRASE'],
113
+ salt: ENV['ENVAULT_SALT'],
114
+ prefix: ENV['ENVAULT_PREFIX']
115
+ }
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,7 @@
1
+ module Envault
2
+ class Environment < Dotenv::Environment
3
+ def initialize(hash)
4
+ update hash
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,15 @@
1
+ module Envault
2
+ class Formatter
3
+ def self.escape_yaml(hash)
4
+ lines = []
5
+ hash.map do |k, v|
6
+ lines << %Q{#{k}: #{v.inspect}}
7
+ end
8
+ lines.join("\n")
9
+ end
10
+
11
+ def self.write_escape_yaml(path, hash)
12
+ File.write(path, escape_yaml(hash))
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,4 @@
1
+ module Envault
2
+ # envault version
3
+ VERSION = "0.1.0"
4
+ end
data/spec/cli_spec.rb ADDED
@@ -0,0 +1,39 @@
1
+ require 'spec_helper'
2
+ require 'envault'
3
+
4
+ describe Envault::CLI do
5
+ before do
6
+ clear_env
7
+ ENV['ENVAULT_PASSPHRASE'] = 'ENV'
8
+ ENV['ENVAULT_SIGN_PASSPHRASE'] = 'ENV'
9
+ ENV['ENVAULT_SALT'] = 'ENV'
10
+ end
11
+
12
+ it "should stdout help" do
13
+ output = capture_stdout do
14
+ Envault::CLI.start(['help'])
15
+ end
16
+ expect(output).not_to eq(nil)
17
+ end
18
+
19
+ it "encrypt options" do
20
+ output = capture_stdout do
21
+ Envault::CLI.start(['help', 'encrypt'])
22
+ end
23
+ expect(output).to include('-s,')
24
+ expect(output).to include('-c,')
25
+ expect(output).to include('-p,')
26
+ end
27
+
28
+ it "decrypt options" do
29
+ output = capture_stdout do
30
+ Envault::CLI.start(['help', 'decrypt'])
31
+ end
32
+ expect(output).to include('-s,')
33
+ expect(output).to include('-c,')
34
+ expect(output).to include('-p,')
35
+ end
36
+
37
+ after do
38
+ end
39
+ end
@@ -0,0 +1,35 @@
1
+ a:
2
+ passphrase: 'p1'
3
+ sign_passphrase: 'sp1'
4
+ salt: 's1'
5
+ prefix: ENVAULT_
6
+
7
+ a1:
8
+ sign_passphrase: 'sp1'
9
+ salt: 's1'
10
+ prefix: ENVAULT_
11
+
12
+ a2:
13
+ passphrase: 'p1'
14
+ prefix: ENVAULT_
15
+
16
+ a3:
17
+ passphrase: 'p1'
18
+ sign_passphrase: 'sp1'
19
+ prefix: ENVAULT_
20
+
21
+ a4:
22
+ passphrase: 'p1'
23
+ salt: 's1'
24
+ prefix: ENVAULT_
25
+
26
+ a5:
27
+ salt: 's1'
28
+ prefix: ENVAULT_
29
+
30
+ b:
31
+ passphrase: 'p2'
32
+ sign_passphrase: 'sp2'
33
+ salt: 's2'
34
+ prefix: ENVAULT_
35
+
data/spec/core_spec.rb ADDED
@@ -0,0 +1,145 @@
1
+ require 'spec_helper'
2
+ require 'envault'
3
+
4
+ describe Envault::Core do
5
+
6
+ before do
7
+ clear_env
8
+ @config_path = File.expand_path("../config/envault.yml", __FILE__)
9
+ @core = Core.new(config: @config_path, profile: 'a')
10
+ @envs = {
11
+ 'USERNAME_A' => 'hogehoge',
12
+ 'PASSWORD_A' => 'hogehoge',
13
+ 'API_KEY_A' => 'fugafuga'
14
+ }
15
+ @encrypted = @core.encrypt_process(@envs, ['PASSWORD_A', 'API_KEY_A'])
16
+ end
17
+
18
+ it "core not nil" do
19
+ expect(@core).not_to eq(nil)
20
+ end
21
+
22
+ it "exist logger" do
23
+ expect(@core.logger).not_to eq(nil)
24
+ end
25
+
26
+ describe 'profile ok case' do
27
+ it "encrypt envs" do
28
+ expect(@encrypted.keys).to eq(['USERNAME_A', 'ENVAULT_PASSWORD_A', 'ENVAULT_API_KEY_A'])
29
+ expect(@encrypted['USERNAME_A']).to eq('hogehoge')
30
+ expect(@encrypted['ENVAULT_PASSWORD_A']).not_to eq('hogehoge')
31
+ expect(@encrypted['ENVAULT_API_KEY_A']).not_to eq('hogehoge')
32
+ end
33
+
34
+ it "decrypt envs" do
35
+ expect(@core.decrypt_process(@encrypted)).to include(@envs)
36
+ end
37
+
38
+ it "decrypt envs other instance" do
39
+ other = Core.new(config: @config_path, profile: 'a')
40
+ expect(other.decrypt_process(@encrypted)).to include(@envs)
41
+ end
42
+ end
43
+
44
+ describe 'profile error case' do
45
+ it "cannot encrypt 1" do
46
+ other = Core.new(config: @config_path, profile: 'a1')
47
+ expect {
48
+ other.decrypt_process(@encrypted)
49
+ }.to raise_exception(ActiveSupport::MessageEncryptor::InvalidMessage)
50
+ end
51
+
52
+ it "cannot encrypt 2" do
53
+ other = Core.new(config: @config_path, profile: 'a2')
54
+ expect {
55
+ other.decrypt_process(@encrypted)
56
+ }.to raise_exception(ActiveSupport::MessageVerifier::InvalidSignature)
57
+ end
58
+
59
+ it "cannot encrypt 3 no salt" do
60
+ other = Core.new(config: @config_path, profile: 'a3')
61
+ expect {
62
+ other.decrypt_process(@encrypted)
63
+ }.to raise_exception(ActiveSupport::MessageVerifier::InvalidSignature)
64
+ end
65
+
66
+ it "cannot encrypt 4 different sign_passphrase" do
67
+ other = Core.new(config: @config_path, profile: 'a4')
68
+ expect {
69
+ other.decrypt_process(@encrypted)
70
+ }.to raise_exception(ActiveSupport::MessageVerifier::InvalidSignature)
71
+ end
72
+
73
+ it "cannot encrypt 5 different sign_passphrase" do
74
+ other = Core.new(config: @config_path, profile: 'a5')
75
+ expect {
76
+ other.decrypt_process(@encrypted)
77
+ }.to raise_exception(ActiveSupport::MessageVerifier::InvalidSignature)
78
+ end
79
+
80
+ it "cannot encrypt" do
81
+ other = Core.new(config: @config_path, profile: 'b')
82
+ expect {
83
+ other.decrypt_process(@encrypted)
84
+ }.to raise_exception(ActiveSupport::MessageVerifier::InvalidSignature)
85
+ end
86
+ end
87
+
88
+ describe 'environment variables' do
89
+ it "environment variables decrypt" do
90
+ ENV['ENVAULT_PASSPHRASE'] = 'p1'
91
+ ENV['ENVAULT_SIGN_PASSPHRASE'] = 'sp1'
92
+ ENV['ENVAULT_SALT'] = 's1'
93
+ ENV['ENVAULT_PREFIX'] = 'ENVAULT_'
94
+ other = Core.new
95
+ expect(other.decrypt_process(@encrypted)).to include(@envs)
96
+ clear_env
97
+ end
98
+
99
+ it "environment variables decrypt other prefix" do
100
+ ENV['ENVAULT_PASSPHRASE'] = 'p1'
101
+ ENV['ENVAULT_PREFIX'] = 'ENVAULT_OTHER_PREFIX_'
102
+ other = Core.new
103
+ decrypted = other.decrypt_process(@encrypted)
104
+ expect(@encrypted['USERNAME_A']).to eq('hogehoge')
105
+ expect(decrypted['ENVAULT_PASSWORD_A']).not_to eq('hogehoge')
106
+ expect(decrypted['ENVAULT_API_KEY_A']).not_to eq('fugafuga')
107
+ clear_env
108
+ end
109
+
110
+ it "environment variables cannot decrypt" do
111
+ ENV['ENVAULT_PASSPHRASE'] = 'p1'
112
+ ENV['ENVAULT_PREFIX'] = 'ENVAULT_'
113
+ other = Core.new
114
+ expect {
115
+ other.decrypt_process(@encrypted)
116
+ }.to raise_exception(ActiveSupport::MessageVerifier::InvalidSignature)
117
+ clear_env
118
+ end
119
+
120
+ it "environment variables cannot decrypt no salt" do
121
+ ENV['ENVAULT_PASSPHRASE'] = 'p1'
122
+ ENV['ENVAULT_SIGN_PASSPHRASE'] = 'sp1'
123
+ ENV['ENVAULT_PREFIX'] = 'ENVAULT_'
124
+ other = Core.new
125
+ expect {
126
+ other.decrypt_process(@encrypted)
127
+ }.to raise_exception(ActiveSupport::MessageVerifier::InvalidSignature)
128
+ clear_env
129
+ end
130
+
131
+ it "environment variables cannot decrypt no passphrase" do
132
+ ENV['ENVAULT_SIGN_PASSPHRASE'] = 'sp1'
133
+ ENV['ENVAULT_SALT'] = 's1'
134
+ ENV['ENVAULT_PREFIX'] = 'ENVAULT_'
135
+ other = Core.new
136
+ expect {
137
+ other.decrypt_process(@encrypted)
138
+ }.to raise_exception(ActiveSupport::MessageEncryptor::InvalidMessage)
139
+ clear_env
140
+ end
141
+ end
142
+
143
+ after do
144
+ end
145
+ end
@@ -0,0 +1,8 @@
1
+ require 'spec_helper'
2
+ require 'envault'
3
+
4
+ describe Envault do
5
+ it "should have a VERSION constant" do
6
+ expect(subject.const_get('VERSION')).to_not be_empty
7
+ end
8
+ end
@@ -0,0 +1,15 @@
1
+ require 'spec_helper'
2
+ require 'envault'
3
+
4
+ describe Envault do
5
+ it "escape_yaml" do
6
+ @envs = {
7
+ 'USERNAME_A' => 'hogehoge',
8
+ 'PASSWORD_A' => 'hogehoge',
9
+ 'API_KEY_A' => 'fugafuga'
10
+ }
11
+
12
+ yaml = Formatter.escape_yaml(@envs)
13
+ expect(yaml).to eq("USERNAME_A: \"hogehoge\"\nPASSWORD_A: \"hogehoge\"\nAPI_KEY_A: \"fugafuga\"")
14
+ end
15
+ end
@@ -0,0 +1,29 @@
1
+ require 'rspec'
2
+ require 'envault/version'
3
+
4
+ include Envault
5
+
6
+ def capture_stdout
7
+ out = StringIO.new
8
+ $stdout = out
9
+ yield
10
+ return out.string
11
+ ensure
12
+ $stdout = STDOUT
13
+ end
14
+
15
+ def capture_stderr
16
+ out = StringIO.new
17
+ $stderr = out
18
+ yield
19
+ return out.string
20
+ ensure
21
+ $stderr = STDERR
22
+ end
23
+
24
+ def clear_env
25
+ ENV['ENVAULT_PASSPHRASE'] = nil
26
+ ENV['ENVAULT_SIGN_PASSPHRASE'] = nil
27
+ ENV['ENVAULT_SALT'] = nil
28
+ ENV['ENVAULT_PREFIX'] = nil
29
+ end
metadata ADDED
@@ -0,0 +1,221 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: envault
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - toyama0919
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-09-16 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: thor
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: dotenv
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: activesupport
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '4'
48
+ - - "<"
49
+ - !ruby/object:Gem::Version
50
+ version: '5'
51
+ type: :runtime
52
+ prerelease: false
53
+ version_requirements: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: '4'
58
+ - - "<"
59
+ - !ruby/object:Gem::Version
60
+ version: '5'
61
+ - !ruby/object:Gem::Dependency
62
+ name: bundler
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ type: :development
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: '0'
75
+ - !ruby/object:Gem::Dependency
76
+ name: pry
77
+ requirement: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ type: :development
83
+ prerelease: false
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ - !ruby/object:Gem::Dependency
90
+ name: rake
91
+ requirement: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ type: :development
97
+ prerelease: false
98
+ version_requirements: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ - !ruby/object:Gem::Dependency
104
+ name: rspec
105
+ requirement: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - "~>"
108
+ - !ruby/object:Gem::Version
109
+ version: '3.0'
110
+ type: :development
111
+ prerelease: false
112
+ version_requirements: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - "~>"
115
+ - !ruby/object:Gem::Version
116
+ version: '3.0'
117
+ - !ruby/object:Gem::Dependency
118
+ name: rubocop
119
+ requirement: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - "~>"
122
+ - !ruby/object:Gem::Version
123
+ version: 0.24.1
124
+ type: :development
125
+ prerelease: false
126
+ version_requirements: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - "~>"
129
+ - !ruby/object:Gem::Version
130
+ version: 0.24.1
131
+ - !ruby/object:Gem::Dependency
132
+ name: rubygems-tasks
133
+ requirement: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - - "~>"
136
+ - !ruby/object:Gem::Version
137
+ version: '0.2'
138
+ type: :development
139
+ prerelease: false
140
+ version_requirements: !ruby/object:Gem::Requirement
141
+ requirements:
142
+ - - "~>"
143
+ - !ruby/object:Gem::Version
144
+ version: '0.2'
145
+ - !ruby/object:Gem::Dependency
146
+ name: yard
147
+ requirement: !ruby/object:Gem::Requirement
148
+ requirements:
149
+ - - "~>"
150
+ - !ruby/object:Gem::Version
151
+ version: '0.8'
152
+ type: :development
153
+ prerelease: false
154
+ version_requirements: !ruby/object:Gem::Requirement
155
+ requirements:
156
+ - - "~>"
157
+ - !ruby/object:Gem::Version
158
+ version: '0.8'
159
+ description: Encrypt secret information environment variables by yaml.
160
+ email: toyama0919@gmail.com
161
+ executables:
162
+ - envault
163
+ extensions: []
164
+ extra_rdoc_files: []
165
+ files:
166
+ - ".document"
167
+ - ".gitignore"
168
+ - ".rspec"
169
+ - ".travis.yml"
170
+ - ".yardopts"
171
+ - ChangeLog.md
172
+ - Gemfile
173
+ - LICENSE.txt
174
+ - README.md
175
+ - Rakefile
176
+ - bin/envault
177
+ - envault.gemspec
178
+ - lib/envault.rb
179
+ - lib/envault/cli.rb
180
+ - lib/envault/constants.rb
181
+ - lib/envault/core.rb
182
+ - lib/envault/environment.rb
183
+ - lib/envault/formatter.rb
184
+ - lib/envault/version.rb
185
+ - spec/cli_spec.rb
186
+ - spec/config/envault.yml
187
+ - spec/core_spec.rb
188
+ - spec/envault_spec.rb
189
+ - spec/formatter_spec.rb
190
+ - spec/spec_helper.rb
191
+ homepage: https://github.com/toyama0919/envault
192
+ licenses:
193
+ - MIT
194
+ metadata: {}
195
+ post_install_message:
196
+ rdoc_options: []
197
+ require_paths:
198
+ - lib
199
+ required_ruby_version: !ruby/object:Gem::Requirement
200
+ requirements:
201
+ - - ">="
202
+ - !ruby/object:Gem::Version
203
+ version: '2.1'
204
+ required_rubygems_version: !ruby/object:Gem::Requirement
205
+ requirements:
206
+ - - ">="
207
+ - !ruby/object:Gem::Version
208
+ version: '0'
209
+ requirements: []
210
+ rubyforge_project:
211
+ rubygems_version: 2.5.1
212
+ signing_key:
213
+ specification_version: 4
214
+ summary: Encrypt secret information environment variables by yaml.
215
+ test_files:
216
+ - spec/cli_spec.rb
217
+ - spec/config/envault.yml
218
+ - spec/core_spec.rb
219
+ - spec/envault_spec.rb
220
+ - spec/formatter_spec.rb
221
+ - spec/spec_helper.rb