secure_credentials 0.1.1 → 0.2.1
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/.gitignore +2 -0
- data/.travis.yml +3 -7
- data/Appraisals +7 -0
- data/CHANGELOG.md +5 -0
- data/Gemfile +2 -0
- data/README.md +16 -15
- data/gemfiles/rails_5.1.gemfile +13 -0
- data/gemfiles/rails_5.2.gemfile +13 -0
- data/lib/secure_credentials/active_support/encrypted_file.rb +104 -0
- data/lib/secure_credentials/encrypted_file.rb +5 -1
- data/lib/secure_credentials/version.rb +1 -1
- data/secure_credentials.gemspec +2 -2
- metadata +12 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3f105345f86816004375c9c6aec5e6d48330f14768e4da243f0f0edf0894341b
|
4
|
+
data.tar.gz: c0a64940f0b7d72c8f338797dc4d35dd1493c88f8fdb2abd733ba6d7bb152947
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 36475092d3f12f08d1ab749694318bafe52c88a84b873149ab51fe3e13eaddd8dea317d1c05d541ec58a692a9c984d63a33cfe01c6094f64bc1484f71f265760
|
7
|
+
data.tar.gz: 6234c6c383e3ceff2c6db4f502e11a2325d7d61c8b1511588205d50f5750bbd90ebc26da5f0d631d7b67bc7aca246914abce0a0cecf0e9dc7a0873676fcecd74
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
@@ -4,13 +4,9 @@ cache: bundler
|
|
4
4
|
rvm:
|
5
5
|
- 2.5
|
6
6
|
- 2.4
|
7
|
+
gemfile:
|
8
|
+
- gemfiles/rails_5.2.gemfile
|
9
|
+
- gemfiles/rails_5.1.gemfile
|
7
10
|
|
8
11
|
notifications:
|
9
12
|
email: false
|
10
|
-
|
11
|
-
# for 2.5.0 until 2.5.1 is released:
|
12
|
-
# https://github.com/travis-ci/travis-ci/issues/8978
|
13
|
-
# https://github.com/travis-ci/travis-ci/issues/8969#issuecomment-354135622
|
14
|
-
before_install:
|
15
|
-
- gem update --system
|
16
|
-
- gem install bundler
|
data/Appraisals
ADDED
data/CHANGELOG.md
ADDED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -4,30 +4,29 @@
|
|
4
4
|
[](https://codeclimate.com/github/printercu/secure_credentials)
|
5
5
|
[](https://travis-ci.org/printercu/secure_credentials)
|
6
6
|
|
7
|
-
Makes it possible to use best of encrypted credentials
|
8
|
-
and environment-dependent secrets. Sharing encryption keys with
|
9
|
-
every developer in a team is a security issue, and purpose of this gem
|
10
|
-
is to help you to avoid it.
|
11
|
-
|
12
7
|
## Rationale
|
13
8
|
|
14
9
|
Rails 5.2 brings good idea of storing encrypted credentials in the repo:
|
15
|
-
credentials are securely tracked in version control, less
|
10
|
+
credentials are securely tracked in version control, less chance to face an issue
|
16
11
|
during deployment, etc. However there are several drawbacks in current implementation:
|
17
12
|
|
18
13
|
- It's hard to manage environment-specific credentials.
|
19
|
-
For example, use
|
14
|
+
For example, to use different browser api keys in development and production,
|
20
15
|
one is whitelisted for `locahost` and other one for app's domain.
|
21
16
|
- In most cases it's required to share `master.key` with every developer.
|
22
17
|
This is not acceptable for a lot of teams, and framework must serve their needs too.
|
23
18
|
|
24
19
|
There are a couple ways to workaround this issues, but all of them brings
|
25
|
-
unnecessary complexity. This gem takes best from new encrypted `credentials.yml.enc`
|
26
|
-
and multi-environmental `secrets.yml
|
20
|
+
unnecessary complexity. This gem takes best from new encrypted credentials (`credentials.yml.enc`)
|
21
|
+
and multi-environmental secrets (`secrets.yml`). It allows to use combination
|
27
22
|
of encrypted and plain files for same configuration in different environments.
|
28
23
|
For example, having encrypted `credentials.production.yml.enc` for production
|
29
24
|
and multi-environmental `credentials.yml` for all other environments.
|
30
25
|
|
26
|
+
There are some other issues caused by storing `master.key` in local repo.
|
27
|
+
See this wiki page for details:<br>
|
28
|
+
[Rails 5.2 credentials are not secure](https://github.com/printercu/secure_credentials/wiki/Rails-5.2-credentials-are-not-secure).
|
29
|
+
|
31
30
|
## Installation
|
32
31
|
|
33
32
|
Add this line to your application's Gemfile:
|
@@ -45,7 +44,7 @@ And then execute:
|
|
45
44
|
By default this gem patches Rails::Application to make `#credentials`, `#secrets` and `#encrypted`
|
46
45
|
use Rails-compatible wrapper around SecureCredentials::Store.
|
47
46
|
|
48
|
-
SecureCredentials::Store provides read-write
|
47
|
+
SecureCredentials::Store provides read-write access to YAML configuration files. It supports:
|
49
48
|
|
50
49
|
- both encrypted and plain files,
|
51
50
|
- both file-per-environment and multi-environment files.
|
@@ -65,26 +64,28 @@ Otherwise `env` is used to fetch appropriate section.
|
|
65
64
|
Key for decoding encoded files can be passed:
|
66
65
|
|
67
66
|
- in `key` argument;
|
68
|
-
- envvar identified by `env_key`, default is to upcased basename appended with `_KEY`
|
67
|
+
- in envvar identified by `env_key`, default is to upcased basename appended with `_KEY`
|
69
68
|
(ex., `SECRETS_KEY`);
|
70
69
|
- in file found at `key_path`,
|
71
70
|
by default it uses filename and replaces `.yml.enc` with `.key`
|
72
71
|
(`secrets.production.key` for `secrets.production.yml.enc`);
|
73
72
|
- `SecureCredentials.master_key` which is read from `config/master.key` in Rails apps.
|
74
73
|
|
75
|
-
|
74
|
+
To edit encrypted files use `rails encrypted:edit path/to/file.yml.enc -k path/to/key.key`.
|
76
75
|
Missing `.key` and `.yml` files are automatically created when you edit them for the first time.
|
77
76
|
|
78
77
|
## Best practices
|
79
78
|
|
80
|
-
- __Don't keep master.key in local
|
79
|
+
- __Don't keep master.key in local working directory!__
|
81
80
|
|
82
|
-
It's
|
81
|
+
It's like a PIN-code written on backside of credit card.
|
83
82
|
Keep it in secure place and use it when you need to modify credentials.
|
84
83
|
|
85
|
-
- Don't share production credentials with those who
|
84
|
+
- Don't share production credentials with those team members who don't need to access them.
|
86
85
|
|
87
86
|
Secrets get less secret every time they are shared.
|
87
|
+
It's better to share some particular keys to selected developers,
|
88
|
+
instead of giving everybody access to all keys.
|
88
89
|
|
89
90
|
## Development
|
90
91
|
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# This file was generated by Appraisal
|
2
|
+
|
3
|
+
source "https://rubygems.org"
|
4
|
+
|
5
|
+
gem "appraisal"
|
6
|
+
gem "rspec", "~> 3.7"
|
7
|
+
gem "rspec-its", "~> 1.2"
|
8
|
+
gem "rubocop", "~> 0.56"
|
9
|
+
gem "pry", "~> 0.11"
|
10
|
+
gem "pry-byebug", "~> 3.6"
|
11
|
+
gem "activesupport", "~> 5.1.0"
|
12
|
+
|
13
|
+
gemspec path: "../"
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# This file was generated by Appraisal
|
2
|
+
|
3
|
+
source "https://rubygems.org"
|
4
|
+
|
5
|
+
gem "appraisal"
|
6
|
+
gem "rspec", "~> 3.7"
|
7
|
+
gem "rspec-its", "~> 1.2"
|
8
|
+
gem "rubocop", "~> 0.56"
|
9
|
+
gem "pry", "~> 0.11"
|
10
|
+
gem "pry-byebug", "~> 3.6"
|
11
|
+
gem "activesupport", "~> 5.2.0"
|
12
|
+
|
13
|
+
gemspec path: "../"
|
@@ -0,0 +1,104 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copied from activesupport 5.2
|
4
|
+
# rubocop:disable all
|
5
|
+
|
6
|
+
require "pathname"
|
7
|
+
require "active_support/message_encryptor"
|
8
|
+
|
9
|
+
module ActiveSupport
|
10
|
+
class EncryptedFile
|
11
|
+
class MissingContentError < RuntimeError
|
12
|
+
def initialize(content_path)
|
13
|
+
super "Missing encrypted content file in #{content_path}."
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class MissingKeyError < RuntimeError
|
18
|
+
def initialize(key_path:, env_key:)
|
19
|
+
super \
|
20
|
+
"Missing encryption key to decrypt file with. " +
|
21
|
+
"Ask your team for your master key and write it to #{key_path} or put it in the ENV['#{env_key}']."
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
CIPHER = "aes-128-gcm"
|
26
|
+
|
27
|
+
def self.generate_key
|
28
|
+
SecureRandom.hex(ActiveSupport::MessageEncryptor.key_len(CIPHER))
|
29
|
+
end
|
30
|
+
|
31
|
+
|
32
|
+
attr_reader :content_path, :key_path, :env_key, :raise_if_missing_key
|
33
|
+
|
34
|
+
def initialize(content_path:, key_path:, env_key:, raise_if_missing_key:)
|
35
|
+
@content_path, @key_path = Pathname.new(content_path), Pathname.new(key_path)
|
36
|
+
@env_key, @raise_if_missing_key = env_key, raise_if_missing_key
|
37
|
+
end
|
38
|
+
|
39
|
+
def key
|
40
|
+
read_env_key || read_key_file || handle_missing_key
|
41
|
+
end
|
42
|
+
|
43
|
+
def read
|
44
|
+
if !key.nil? && content_path.exist?
|
45
|
+
decrypt content_path.binread
|
46
|
+
else
|
47
|
+
raise MissingContentError, content_path
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def write(contents)
|
52
|
+
IO.binwrite "#{content_path}.tmp", encrypt(contents)
|
53
|
+
FileUtils.mv "#{content_path}.tmp", content_path
|
54
|
+
end
|
55
|
+
|
56
|
+
def change(&block)
|
57
|
+
writing read, &block
|
58
|
+
end
|
59
|
+
|
60
|
+
|
61
|
+
private
|
62
|
+
def writing(contents)
|
63
|
+
tmp_file = "#{Process.pid}.#{content_path.basename.to_s.chomp('.enc')}"
|
64
|
+
tmp_path = Pathname.new File.join(Dir.tmpdir, tmp_file)
|
65
|
+
tmp_path.binwrite contents
|
66
|
+
|
67
|
+
yield tmp_path
|
68
|
+
|
69
|
+
updated_contents = tmp_path.binread
|
70
|
+
|
71
|
+
write(updated_contents) if updated_contents != contents
|
72
|
+
ensure
|
73
|
+
FileUtils.rm(tmp_path) if tmp_path.exist?
|
74
|
+
end
|
75
|
+
|
76
|
+
|
77
|
+
def encrypt(contents)
|
78
|
+
encryptor.encrypt_and_sign contents
|
79
|
+
end
|
80
|
+
|
81
|
+
def decrypt(contents)
|
82
|
+
encryptor.decrypt_and_verify contents
|
83
|
+
end
|
84
|
+
|
85
|
+
def encryptor
|
86
|
+
@encryptor ||= ActiveSupport::MessageEncryptor.new([ key ].pack("H*"), cipher: CIPHER)
|
87
|
+
end
|
88
|
+
|
89
|
+
|
90
|
+
def read_env_key
|
91
|
+
ENV[env_key]
|
92
|
+
end
|
93
|
+
|
94
|
+
def read_key_file
|
95
|
+
key_path.binread.strip if key_path.exist?
|
96
|
+
end
|
97
|
+
|
98
|
+
def handle_missing_key
|
99
|
+
raise MissingKeyError, key_path: key_path, env_key: env_key if raise_if_missing_key
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
# rubocop:enable all
|
@@ -1,5 +1,9 @@
|
|
1
1
|
require 'securerandom'
|
2
|
-
|
2
|
+
begin
|
3
|
+
require 'active_support/encrypted_file'
|
4
|
+
rescue LoadError
|
5
|
+
require 'secure_credentials/active_support/encrypted_file'
|
6
|
+
end
|
3
7
|
|
4
8
|
module SecureCredentials
|
5
9
|
# Wraps ActiveSupport::EncryptedFile and provides passing key as an argument.
|
data/secure_credentials.gemspec
CHANGED
@@ -19,8 +19,8 @@ Gem::Specification.new do |spec|
|
|
19
19
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
20
20
|
spec.require_paths = ['lib']
|
21
21
|
|
22
|
-
spec.add_dependency 'activesupport', '~> 5.
|
22
|
+
spec.add_dependency 'activesupport', '~> 5.1'
|
23
23
|
|
24
|
-
spec.add_development_dependency 'bundler', '
|
24
|
+
spec.add_development_dependency 'bundler', '> 1.16'
|
25
25
|
spec.add_development_dependency 'rake', '~> 10.0'
|
26
26
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: secure_credentials
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Max Melentiev
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2019-03-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -16,26 +16,26 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '5.
|
19
|
+
version: '5.1'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '5.
|
26
|
+
version: '5.1'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: bundler
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- - "
|
31
|
+
- - ">"
|
32
32
|
- !ruby/object:Gem::Version
|
33
33
|
version: '1.16'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- - "
|
38
|
+
- - ">"
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '1.16'
|
41
41
|
- !ruby/object:Gem::Dependency
|
@@ -64,6 +64,8 @@ files:
|
|
64
64
|
- ".rspec"
|
65
65
|
- ".rubocop.yml"
|
66
66
|
- ".travis.yml"
|
67
|
+
- Appraisals
|
68
|
+
- CHANGELOG.md
|
67
69
|
- Gemfile
|
68
70
|
- README.md
|
69
71
|
- Rakefile
|
@@ -71,7 +73,10 @@ files:
|
|
71
73
|
- bin/git-hooks/pre-commit
|
72
74
|
- bin/install_git_hooks
|
73
75
|
- bin/setup
|
76
|
+
- gemfiles/rails_5.1.gemfile
|
77
|
+
- gemfiles/rails_5.2.gemfile
|
74
78
|
- lib/secure_credentials.rb
|
79
|
+
- lib/secure_credentials/active_support/encrypted_file.rb
|
75
80
|
- lib/secure_credentials/credentials.rb
|
76
81
|
- lib/secure_credentials/encrypted_file.rb
|
77
82
|
- lib/secure_credentials/no_rails_patch.rb
|
@@ -98,8 +103,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
98
103
|
- !ruby/object:Gem::Version
|
99
104
|
version: '0'
|
100
105
|
requirements: []
|
101
|
-
|
102
|
-
rubygems_version: 2.7.6
|
106
|
+
rubygems_version: 3.0.3
|
103
107
|
signing_key:
|
104
108
|
specification_version: 4
|
105
109
|
summary: Rails credentials without security issues. With environments support.
|