kms_encrypted 0.1.2 → 0.1.3
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/.travis.yml +13 -0
- data/CHANGELOG.md +6 -0
- data/README.md +80 -8
- data/kms_encrypted.gemspec +1 -1
- data/lib/kms_encrypted.rb +37 -23
- data/lib/kms_encrypted/version.rb +1 -1
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 48601fd1b07d25fafe4a14a508511bd3237ef486
|
4
|
+
data.tar.gz: 74add3ece85f95c4e1a2b5483b772c2075def8c4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1461f4cbdd77bb98eba915832ee402d1467f8ed5b3578295010ece703864da16418cc7925f78fc64ffc6ed1176e34c07cc5912556fea8b2f6726b872b669e56b
|
7
|
+
data.tar.gz: 6268c77a1f065ce79ebf1a2cf954a7d36265d96135b54be8aeabec96d5b72828b8929f69dcc2778bbee3eda8247f1735747af22ed6a814fdf2428fa5d5510673
|
data/.travis.yml
ADDED
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -1,15 +1,17 @@
|
|
1
1
|
# KMS Encrypted
|
2
2
|
|
3
|
-
|
3
|
+
Simple, secure key management for [attr_encrypted](https://github.com/attr-encrypted/attr_encrypted)
|
4
4
|
|
5
5
|
The attr_encrypted gem is great for encryption, but:
|
6
6
|
|
7
7
|
1. Leaves you to manage the security of your keys
|
8
|
-
2. Doesn’t provide
|
8
|
+
2. Doesn’t provide an easy way to rotate your keys
|
9
|
+
3. Doesn’t have a great audit trail to see how data has been accessed
|
10
|
+
4. Doesn’t let you grant encryption and decryption permission separately
|
9
11
|
|
10
|
-
KMS addresses
|
12
|
+
[KMS](https://aws.amazon.com/kms/) addresses all of these issues and it’s easy to use them together.
|
11
13
|
|
12
|
-
|
14
|
+
[](https://travis-ci.org/ankane/kms_encrypted)
|
13
15
|
|
14
16
|
## How It Works
|
15
17
|
|
@@ -27,18 +29,28 @@ Add this line to your application’s Gemfile:
|
|
27
29
|
gem 'kms_encrypted'
|
28
30
|
```
|
29
31
|
|
30
|
-
Add
|
32
|
+
Add columns for the encrypted data and the encrypted KMS data keys
|
31
33
|
|
32
34
|
```ruby
|
33
|
-
add_column :users, :
|
35
|
+
add_column :users, :encrypted_email, :text
|
36
|
+
add_column :users, :encrypted_email_iv, :text
|
37
|
+
add_column :users, :encrypted_kms_key, :text
|
34
38
|
```
|
35
39
|
|
40
|
+
Create an [Amazon Web Services](https://aws.amazon.com/) account if you don’t have one. KMS works great whether or not you use other AWS services.
|
41
|
+
|
36
42
|
Create a [KMS master key](https://console.aws.amazon.com/iam/home#/encryptionKeys) and set it in your environment ([dotenv](https://github.com/bkeepers/dotenv) is great for this)
|
37
43
|
|
38
44
|
```sh
|
39
45
|
KMS_KEY_ID=arn:aws:kms:...
|
40
46
|
```
|
41
47
|
|
48
|
+
You can also use the alias
|
49
|
+
|
50
|
+
```sh
|
51
|
+
KMS_KEY_ID=alias/my-alias
|
52
|
+
```
|
53
|
+
|
42
54
|
And update your model
|
43
55
|
|
44
56
|
```ruby
|
@@ -137,16 +149,22 @@ Change the last line to point to your CloudTrail log bucket and query away
|
|
137
149
|
```sql
|
138
150
|
SELECT
|
139
151
|
eventTime,
|
140
|
-
eventName,
|
141
152
|
userIdentity.userName,
|
142
153
|
requestParameters
|
143
154
|
FROM
|
144
155
|
cloudtrail_logs
|
145
156
|
WHERE
|
146
157
|
eventName = 'Decrypt'
|
158
|
+
AND resources[1].arn = 'arn:aws:kms:...'
|
147
159
|
ORDER BY 1
|
148
160
|
```
|
149
161
|
|
162
|
+
There will also be `GenerateDataKey` events.
|
163
|
+
|
164
|
+
## Alerting
|
165
|
+
|
166
|
+
We recommend setting up alerts on suspicious behavior.
|
167
|
+
|
150
168
|
## Key Rotation
|
151
169
|
|
152
170
|
KMS supports [automatic key rotation](http://docs.aws.amazon.com/kms/latest/developerguide/rotate-keys.html). No action is required in this case.
|
@@ -165,6 +183,58 @@ User.find_each do |user|
|
|
165
183
|
end
|
166
184
|
```
|
167
185
|
|
186
|
+
## IAM Permissions
|
187
|
+
|
188
|
+
A great feature of KMS is the ability to grant encryption and decryption permission separately.
|
189
|
+
|
190
|
+
To encrypt the data, use a policy with:
|
191
|
+
|
192
|
+
```json
|
193
|
+
{
|
194
|
+
"Version": "2012-10-17",
|
195
|
+
"Statement": [
|
196
|
+
{
|
197
|
+
"Sid": "EncryptData",
|
198
|
+
"Effect": "Allow",
|
199
|
+
"Action": "kms:GenerateDataKey",
|
200
|
+
"Resource": "arn:aws:kms:..."
|
201
|
+
}
|
202
|
+
]
|
203
|
+
}
|
204
|
+
```
|
205
|
+
|
206
|
+
If a system can only encrypt, you must clear out existing data keys before updates.
|
207
|
+
|
208
|
+
```ruby
|
209
|
+
user.encrypted_kms_key = nil # before user.save
|
210
|
+
```
|
211
|
+
|
212
|
+
To decrypt the data, use a policy with:
|
213
|
+
|
214
|
+
```json
|
215
|
+
{
|
216
|
+
"Version": "2012-10-17",
|
217
|
+
"Statement": [
|
218
|
+
{
|
219
|
+
"Sid": "DecryptData",
|
220
|
+
"Effect": "Allow",
|
221
|
+
"Action": "kms:Decrypt",
|
222
|
+
"Resource": "arn:aws:kms:..."
|
223
|
+
}
|
224
|
+
]
|
225
|
+
}
|
226
|
+
```
|
227
|
+
|
228
|
+
Be extremely selective of systems you allow to decrypt.
|
229
|
+
|
230
|
+
## Testing
|
231
|
+
|
232
|
+
For testing, you can prevent network calls to KMS by setting:
|
233
|
+
|
234
|
+
```sh
|
235
|
+
KMS_KEY_ID=insecure-test-key
|
236
|
+
```
|
237
|
+
|
168
238
|
## Multiple Keys Per Record
|
169
239
|
|
170
240
|
You may want to protect different columns with different data keys (or even master keys).
|
@@ -172,7 +242,9 @@ You may want to protect different columns with different data keys (or even mast
|
|
172
242
|
To do this, add more columns
|
173
243
|
|
174
244
|
```ruby
|
175
|
-
add_column :users, :
|
245
|
+
add_column :users, :encrypted_phone, :text
|
246
|
+
add_column :users, :encrypted_phone_iv, :text
|
247
|
+
add_column :users, :encrypted_kms_key_phone, :text
|
176
248
|
```
|
177
249
|
|
178
250
|
And update your model
|
data/kms_encrypted.gemspec
CHANGED
@@ -9,7 +9,7 @@ Gem::Specification.new do |spec|
|
|
9
9
|
spec.authors = ["Andrew Kane"]
|
10
10
|
spec.email = ["andrew@chartkick.com"]
|
11
11
|
|
12
|
-
spec.summary = "
|
12
|
+
spec.summary = "Simple, secure key management for attr_encrypted"
|
13
13
|
spec.homepage = "https://github.com/ankane/kms_encrypted"
|
14
14
|
|
15
15
|
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
data/lib/kms_encrypted.rb
CHANGED
@@ -3,45 +3,59 @@ require "active_support"
|
|
3
3
|
require "aws-sdk-kms"
|
4
4
|
|
5
5
|
module KmsEncrypted
|
6
|
+
class << self
|
7
|
+
attr_accessor :client_options
|
8
|
+
end
|
9
|
+
self.client_options = {
|
10
|
+
retry_limit: 2,
|
11
|
+
http_open_timeout: 2,
|
12
|
+
http_read_timeout: 2
|
13
|
+
}
|
14
|
+
|
6
15
|
def self.kms
|
7
|
-
@kms ||= Aws::KMS::Client.new
|
16
|
+
@kms ||= Aws::KMS::Client.new(client_options)
|
8
17
|
end
|
9
18
|
|
10
19
|
module Model
|
11
20
|
def has_kms_key(legacy_key_id = nil, name: nil, key_id: nil)
|
12
21
|
key_id ||= legacy_key_id || ENV["KMS_KEY_ID"]
|
13
|
-
raise ArgumentError, "Missing key id" unless key_id
|
14
22
|
|
15
23
|
key_method = name ? "kms_key_#{name}" : "kms_key"
|
16
24
|
|
17
25
|
class_eval do
|
18
26
|
define_method(key_method) do
|
27
|
+
raise ArgumentError, "Missing key id" unless key_id
|
28
|
+
|
19
29
|
instance_var = "@#{key_method}"
|
20
30
|
|
21
31
|
unless instance_variable_get(instance_var)
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
32
|
+
if key_id == "insecure-test-key"
|
33
|
+
instance_variable_set(instance_var, "00000000000000000000000000000000")
|
34
|
+
else
|
35
|
+
key_column = "encrypted_#{key_method}"
|
36
|
+
context_method = name ? "kms_encryption_context_#{name}" : "kms_encryption_context"
|
37
|
+
context = respond_to?(context_method, true) ? send(context_method) : {}
|
38
|
+
default_encoding = "m"
|
26
39
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
40
|
+
unless send(key_column)
|
41
|
+
resp = KmsEncrypted.kms.generate_data_key(
|
42
|
+
key_id: key_id,
|
43
|
+
encryption_context: context,
|
44
|
+
key_spec: "AES_256"
|
45
|
+
)
|
46
|
+
ciphertext = resp.ciphertext_blob
|
47
|
+
instance_variable_set(instance_var, resp.plaintext)
|
48
|
+
self.send("#{key_column}=", [resp.ciphertext_blob].pack(default_encoding))
|
49
|
+
end
|
37
50
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
51
|
+
unless instance_variable_get(instance_var)
|
52
|
+
ciphertext = send(key_column).unpack(default_encoding).first
|
53
|
+
resp = KmsEncrypted.kms.decrypt(
|
54
|
+
ciphertext_blob: ciphertext,
|
55
|
+
encryption_context: context
|
56
|
+
)
|
57
|
+
instance_variable_set(instance_var, resp.plaintext)
|
58
|
+
end
|
45
59
|
end
|
46
60
|
end
|
47
61
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: kms_encrypted
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrew Kane
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-12-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: aws-sdk-kms
|
@@ -130,6 +130,7 @@ extensions: []
|
|
130
130
|
extra_rdoc_files: []
|
131
131
|
files:
|
132
132
|
- ".gitignore"
|
133
|
+
- ".travis.yml"
|
133
134
|
- CHANGELOG.md
|
134
135
|
- Gemfile
|
135
136
|
- README.md
|
@@ -159,5 +160,5 @@ rubyforge_project:
|
|
159
160
|
rubygems_version: 2.6.13
|
160
161
|
signing_key:
|
161
162
|
specification_version: 4
|
162
|
-
summary:
|
163
|
+
summary: Simple, secure key management for attr_encrypted
|
163
164
|
test_files: []
|