kms_encrypted 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/CHANGELOG.md +3 -0
- data/Gemfile +3 -0
- data/README.md +84 -0
- data/Rakefile +11 -0
- data/kms_encrypted.gemspec +31 -0
- data/lib/kms_encrypted.rb +56 -0
- data/lib/kms_encrypted/version.rb +3 -0
- metadata +163 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: dcfe0e3ccdefdbde5b05b879a8f2badf3337fd0e
|
4
|
+
data.tar.gz: 8866251c5210f0c67845f99a221931db0a77446f
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 664d6f021217bbd3e3740943bc91b60d1ad7af84c6f2081512f03956ba48488a98666a94417a61a1e68a15ccd36a8a4b31c541a6c40422a7cb8f21f079761d0f
|
7
|
+
data.tar.gz: dbd76c23ee501e1e7c8cb28d4cd1e45430e6ea3904ef4afa8053973b034f8637627c01646e9b5782079f24c151702aa2e3006940df99f572b73be9a8adad7a56
|
data/.gitignore
ADDED
data/CHANGELOG.md
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
# KMS Encrypted
|
2
|
+
|
3
|
+
[KMS](https://aws.amazon.com/kms/) + [attr_encrypted](https://github.com/attr-encrypted/attr_encrypted)
|
4
|
+
|
5
|
+
The attr_encrypted gem is great for encryption, but:
|
6
|
+
|
7
|
+
1. Leaves you to manage the security of your keys
|
8
|
+
2. Doesn’t provide a great audit trail to see how data has been accessed
|
9
|
+
|
10
|
+
KMS addresses both of the issues and it’s easy to use them together.
|
11
|
+
|
12
|
+
**Note:** This has not been battle-tested in a production environment, so use with caution
|
13
|
+
|
14
|
+
## Getting Started
|
15
|
+
|
16
|
+
Add this line to your application’s Gemfile:
|
17
|
+
|
18
|
+
```ruby
|
19
|
+
gem 'kms_encrypted'
|
20
|
+
```
|
21
|
+
|
22
|
+
Add a column to store encrypted KMS data keys
|
23
|
+
|
24
|
+
```ruby
|
25
|
+
add_column :users, :encrypted_kms_key, :string
|
26
|
+
```
|
27
|
+
|
28
|
+
Create a [KMS master key](https://console.aws.amazon.com/iam/home#/encryptionKeys) and set it in your environment (we recommend [dotenv](https://github.com/bkeepers/dotenv))
|
29
|
+
|
30
|
+
```sh
|
31
|
+
KMS_KEY_ID=arn:aws:kms:...
|
32
|
+
```
|
33
|
+
|
34
|
+
And update your model
|
35
|
+
|
36
|
+
```ruby
|
37
|
+
class User < ApplicationRecord
|
38
|
+
has_kms_key ENV["KMS_KEY_ID"]
|
39
|
+
|
40
|
+
attr_encrypted :email, key: :kms_key
|
41
|
+
end
|
42
|
+
```
|
43
|
+
|
44
|
+
For each encrypted attribute, use the `kms_key` method for its key.
|
45
|
+
|
46
|
+
## Auditing
|
47
|
+
|
48
|
+
[AWS CloudTrail](https://aws.amazon.com/cloudtrail/) logs all decryption calls. However, to know what data is being decrypted, you’ll need to add context.
|
49
|
+
|
50
|
+
Add a `kms_encryption_context` method to your model.
|
51
|
+
|
52
|
+
```ruby
|
53
|
+
class User < ApplicationRecord
|
54
|
+
def kms_encryption_context
|
55
|
+
self.id ||= self.class.connection.execute("select nextval('#{self.class.sequence_name}')").first["nextval"]
|
56
|
+
{"Record" => "#{model_name}/#{id}"}
|
57
|
+
end
|
58
|
+
end
|
59
|
+
```
|
60
|
+
|
61
|
+
The context is used as part of the encryption and decryption process, so it must be a value that doesn’t change. Otherwise, you won’t be able to decrypt.
|
62
|
+
|
63
|
+
Read more about [encryption context here](http://docs.aws.amazon.com/kms/latest/developerguide/encryption-context.html).
|
64
|
+
|
65
|
+
## How It Works
|
66
|
+
|
67
|
+
KMS allows you to generate data keys from your master key. Data keys are encrypted and stored with each record. Whenever you want to decrypt data, you need to first decrypt the data key with KMS. Once you have the decrypted key, you can then use it to decrypt the data with attr_encrypted.
|
68
|
+
|
69
|
+
## TODO
|
70
|
+
|
71
|
+
- add support for multiple data keys per record
|
72
|
+
|
73
|
+
## History
|
74
|
+
|
75
|
+
View the [changelog](https://github.com/ankane/kms_encrypted/blob/master/CHANGELOG.md)
|
76
|
+
|
77
|
+
## Contributing
|
78
|
+
|
79
|
+
Everyone is encouraged to help improve this project. Here are a few ways you can help:
|
80
|
+
|
81
|
+
- [Report bugs](https://github.com/ankane/kms_encrypted/issues)
|
82
|
+
- Fix bugs and [submit pull requests](https://github.com/ankane/kms_encrypted/pulls)
|
83
|
+
- Write, clarify, or fix documentation
|
84
|
+
- Suggest or add new features
|
data/Rakefile
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path("../lib", __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require "kms_encrypted/version"
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "kms_encrypted"
|
8
|
+
spec.version = KmsEncrypted::VERSION
|
9
|
+
spec.authors = ["Andrew Kane"]
|
10
|
+
spec.email = ["andrew@chartkick.com"]
|
11
|
+
|
12
|
+
spec.summary = "KMS + attr_encrypted"
|
13
|
+
spec.homepage = "https://github.com/ankane/kms_encrypted"
|
14
|
+
|
15
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
16
|
+
f.match(%r{^(test|spec|features)/})
|
17
|
+
end
|
18
|
+
spec.bindir = "exe"
|
19
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
20
|
+
spec.require_paths = ["lib"]
|
21
|
+
|
22
|
+
spec.add_dependency "aws-sdk-kms"
|
23
|
+
spec.add_dependency "activesupport"
|
24
|
+
|
25
|
+
spec.add_development_dependency "bundler"
|
26
|
+
spec.add_development_dependency "rake"
|
27
|
+
spec.add_development_dependency "minitest"
|
28
|
+
spec.add_development_dependency "sqlite3"
|
29
|
+
spec.add_development_dependency "activerecord"
|
30
|
+
spec.add_development_dependency "attr_encrypted"
|
31
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require "kms_encrypted/version"
|
2
|
+
require "active_support"
|
3
|
+
require "aws-sdk-kms"
|
4
|
+
|
5
|
+
module KmsEncrypted
|
6
|
+
def self.kms
|
7
|
+
@kms ||= Aws::KMS::Client.new
|
8
|
+
end
|
9
|
+
|
10
|
+
module Model
|
11
|
+
def has_kms_key(key_id)
|
12
|
+
raise ArgumentError, "Missing key id" unless key_id
|
13
|
+
|
14
|
+
class_eval do
|
15
|
+
class << self
|
16
|
+
attr_accessor :kms_key_id
|
17
|
+
end
|
18
|
+
self.kms_key_id = key_id
|
19
|
+
|
20
|
+
def kms_key
|
21
|
+
unless @kms_key
|
22
|
+
key_id = self.class.kms_key_id
|
23
|
+
context = respond_to?(:kms_encryption_context) ? kms_encryption_context : {}
|
24
|
+
default_encoding = "m"
|
25
|
+
|
26
|
+
unless encrypted_kms_key
|
27
|
+
resp = KmsEncrypted.kms.generate_data_key(
|
28
|
+
key_id: key_id,
|
29
|
+
encryption_context: context,
|
30
|
+
key_spec: "AES_256"
|
31
|
+
)
|
32
|
+
@kms_key = resp.plaintext
|
33
|
+
ciphertext = resp.ciphertext_blob
|
34
|
+
self.encrypted_kms_key = [resp.ciphertext_blob].pack(default_encoding)
|
35
|
+
end
|
36
|
+
|
37
|
+
unless @kms_key
|
38
|
+
ciphertext = encrypted_kms_key.unpack(default_encoding).first
|
39
|
+
resp = KmsEncrypted.kms.decrypt(
|
40
|
+
ciphertext_blob: ciphertext,
|
41
|
+
encryption_context: context
|
42
|
+
)
|
43
|
+
@kms_key = resp.plaintext
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
@kms_key
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
ActiveSupport.on_load(:active_record) do
|
55
|
+
extend KmsEncrypted::Model
|
56
|
+
end
|
metadata
ADDED
@@ -0,0 +1,163 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: kms_encrypted
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Andrew Kane
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-09-24 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: aws-sdk-kms
|
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: activesupport
|
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: bundler
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rake
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: minitest
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: sqlite3
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: activerecord
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: attr_encrypted
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
description:
|
126
|
+
email:
|
127
|
+
- andrew@chartkick.com
|
128
|
+
executables: []
|
129
|
+
extensions: []
|
130
|
+
extra_rdoc_files: []
|
131
|
+
files:
|
132
|
+
- ".gitignore"
|
133
|
+
- CHANGELOG.md
|
134
|
+
- Gemfile
|
135
|
+
- README.md
|
136
|
+
- Rakefile
|
137
|
+
- kms_encrypted.gemspec
|
138
|
+
- lib/kms_encrypted.rb
|
139
|
+
- lib/kms_encrypted/version.rb
|
140
|
+
homepage: https://github.com/ankane/kms_encrypted
|
141
|
+
licenses: []
|
142
|
+
metadata: {}
|
143
|
+
post_install_message:
|
144
|
+
rdoc_options: []
|
145
|
+
require_paths:
|
146
|
+
- lib
|
147
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
148
|
+
requirements:
|
149
|
+
- - ">="
|
150
|
+
- !ruby/object:Gem::Version
|
151
|
+
version: '0'
|
152
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
153
|
+
requirements:
|
154
|
+
- - ">="
|
155
|
+
- !ruby/object:Gem::Version
|
156
|
+
version: '0'
|
157
|
+
requirements: []
|
158
|
+
rubyforge_project:
|
159
|
+
rubygems_version: 2.6.13
|
160
|
+
signing_key:
|
161
|
+
specification_version: 4
|
162
|
+
summary: KMS + attr_encrypted
|
163
|
+
test_files: []
|