portunus 0.3.0
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 +7 -0
- data/.gitignore +3 -0
- data/.nvmrc +1 -0
- data/.travis.yml +27 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +189 -0
- data/LICENSE.txt +21 -0
- data/README.md +289 -0
- data/Rakefile +2 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/Rakefile +4 -0
- data/lib/generators/install_generator.rb +26 -0
- data/lib/generators/templates/create_portunus.rb.erb +28 -0
- data/lib/portunus/configuration.rb +49 -0
- data/lib/portunus/data_encryption_key.rb +25 -0
- data/lib/portunus/data_key_generator.rb +55 -0
- data/lib/portunus/encryptable.rb +42 -0
- data/lib/portunus/encrypters/open_ssl_aes.rb +58 -0
- data/lib/portunus/encrypters/time.rb +1 -0
- data/lib/portunus/field_configurer.rb +157 -0
- data/lib/portunus/hasher.rb +9 -0
- data/lib/portunus/master_key.rb +15 -0
- data/lib/portunus/railtie.rb +14 -0
- data/lib/portunus/rotators/dek.rb +69 -0
- data/lib/portunus/rotators/kek.rb +51 -0
- data/lib/portunus/storage_adaptors/credentials.rb +48 -0
- data/lib/portunus/storage_adaptors/environment.rb +67 -0
- data/lib/portunus/tasks/generate_keys.rake +36 -0
- data/lib/portunus/tasks/rotate_keys.rake +36 -0
- data/lib/portunus/type_caster.rb +37 -0
- data/lib/portunus/type_casters/boolean.rb +39 -0
- data/lib/portunus/type_casters/date.rb +29 -0
- data/lib/portunus/type_casters/date_time.rb +29 -0
- data/lib/portunus/type_casters/float.rb +29 -0
- data/lib/portunus/type_casters/integer.rb +29 -0
- data/lib/portunus/type_casters/string.rb +29 -0
- data/lib/portunus/version.rb +3 -0
- data/lib/portunus.rb +39 -0
- data/portunus.gemspec +51 -0
- data/tmp/log/development.log +0 -0
- metadata +255 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: e68d034c4896eb04c0540e5937ebe6cea88af51bd1ee0b66526d1e84eb92d6db
|
4
|
+
data.tar.gz: fb0c26ef7df5bdfdbf58feec0dbd13b222598f9f07196231fe06351d72d0f89a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 50e8be7b50b816b1c45d6743085004be1b9e6e6b3a64ed1de57836829d46f7d8b1bb5145483cf997b4d633693a7da84b9daea67d4b2484c1a6f88f0b48718a08
|
7
|
+
data.tar.gz: e2966752917367326122e3976662cde66810cfa536c0ebb19c71caf2a65eedb88b5832f7efeb0dac635eb7f19a28823fb0eebccf95553056c139fc40637bc66a
|
data/.gitignore
ADDED
data/.nvmrc
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
v13.5.0
|
data/.travis.yml
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
env:
|
2
|
+
global:
|
3
|
+
- CC_TEST_REPORTER_ID=f40f50c1b06c16f8e703b01facaf313bcd0a7aa56100b9b077648607ca050833
|
4
|
+
cache:
|
5
|
+
bundler: true
|
6
|
+
language: ruby
|
7
|
+
rvm:
|
8
|
+
- 2.5
|
9
|
+
- 2.6
|
10
|
+
- 2.7
|
11
|
+
before_install:
|
12
|
+
- nvm install # Install node version from .nvmrc
|
13
|
+
- curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
|
14
|
+
- chmod +x ./cc-test-reporter
|
15
|
+
install:
|
16
|
+
- bundle install --without production --path=${BUNDLE_PATH:-vendor/bundle}
|
17
|
+
- npm install
|
18
|
+
before_script:
|
19
|
+
- bin/setup
|
20
|
+
- ./cc-test-reporter before-build
|
21
|
+
script:
|
22
|
+
- bundle exec rspec spec/
|
23
|
+
after_script:
|
24
|
+
- ./cc-test-reporter format-coverage -t simplecov -o coverage/codeclimate.backend.json coverage/backend/.resultset.json # Format backend coverage
|
25
|
+
# - ./cc-test-reporter format-coverage -t lcov -o coverage/codeclimate.frontend.json coverage/frontend/lcov.info # Format frontend coverage
|
26
|
+
- ./cc-test-reporter sum-coverage coverage/codeclimate.*.json -p 1 # Sum both coverage parts into coverage/codeclimate.json
|
27
|
+
- if [[ "$TRAVIS_TEST_RESULT" == 0 ]]; then ./cc-test-reporter upload-coverage; fi # Upload coverage/codeclimate.json
|
data/CODE_OF_CONDUCT.md
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
# Contributor Covenant Code of Conduct
|
2
|
+
|
3
|
+
## Our Pledge
|
4
|
+
|
5
|
+
In the interest of fostering an open and welcoming environment, we as
|
6
|
+
contributors and maintainers pledge to making participation in our project and
|
7
|
+
our community a harassment-free experience for everyone, regardless of age, body
|
8
|
+
size, disability, ethnicity, gender identity and expression, level of experience,
|
9
|
+
nationality, personal appearance, race, religion, or sexual identity and
|
10
|
+
orientation.
|
11
|
+
|
12
|
+
## Our Standards
|
13
|
+
|
14
|
+
Examples of behavior that contributes to creating a positive environment
|
15
|
+
include:
|
16
|
+
|
17
|
+
* Using welcoming and inclusive language
|
18
|
+
* Being respectful of differing viewpoints and experiences
|
19
|
+
* Gracefully accepting constructive criticism
|
20
|
+
* Focusing on what is best for the community
|
21
|
+
* Showing empathy towards other community members
|
22
|
+
|
23
|
+
Examples of unacceptable behavior by participants include:
|
24
|
+
|
25
|
+
* The use of sexualized language or imagery and unwelcome sexual attention or
|
26
|
+
advances
|
27
|
+
* Trolling, insulting/derogatory comments, and personal or political attacks
|
28
|
+
* Public or private harassment
|
29
|
+
* Publishing others' private information, such as a physical or electronic
|
30
|
+
address, without explicit permission
|
31
|
+
* Other conduct which could reasonably be considered inappropriate in a
|
32
|
+
professional setting
|
33
|
+
|
34
|
+
## Our Responsibilities
|
35
|
+
|
36
|
+
Project maintainers are responsible for clarifying the standards of acceptable
|
37
|
+
behavior and are expected to take appropriate and fair corrective action in
|
38
|
+
response to any instances of unacceptable behavior.
|
39
|
+
|
40
|
+
Project maintainers have the right and responsibility to remove, edit, or
|
41
|
+
reject comments, commits, code, wiki edits, issues, and other contributions
|
42
|
+
that are not aligned to this Code of Conduct, or to ban temporarily or
|
43
|
+
permanently any contributor for other behaviors that they deem inappropriate,
|
44
|
+
threatening, offensive, or harmful.
|
45
|
+
|
46
|
+
## Scope
|
47
|
+
|
48
|
+
This Code of Conduct applies both within project spaces and in public spaces
|
49
|
+
when an individual is representing the project or its community. Examples of
|
50
|
+
representing a project or community include using an official project e-mail
|
51
|
+
address, posting via an official social media account, or acting as an appointed
|
52
|
+
representative at an online or offline event. Representation of a project may be
|
53
|
+
further defined and clarified by project maintainers.
|
54
|
+
|
55
|
+
## Enforcement
|
56
|
+
|
57
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
58
|
+
reported by contacting the project team at colinpetruno@gmail.com. All
|
59
|
+
complaints will be reviewed and investigated and will result in a response that
|
60
|
+
is deemed necessary and appropriate to the circumstances. The project team is
|
61
|
+
obligated to maintain confidentiality with regard to the reporter of an incident.
|
62
|
+
Further details of specific enforcement policies may be posted separately.
|
63
|
+
|
64
|
+
Project maintainers who do not follow or enforce the Code of Conduct in good
|
65
|
+
faith may face temporary or permanent repercussions as determined by other
|
66
|
+
members of the project's leadership.
|
67
|
+
|
68
|
+
## Attribution
|
69
|
+
|
70
|
+
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
71
|
+
available at [http://contributor-covenant.org/version/1/4][version]
|
72
|
+
|
73
|
+
[homepage]: http://contributor-covenant.org
|
74
|
+
[version]: http://contributor-covenant.org/version/1/4/
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,189 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
portunus (0.3.0)
|
5
|
+
aes
|
6
|
+
openssl (>= 2.1.0)
|
7
|
+
rails (>= 5.0.0)
|
8
|
+
|
9
|
+
GEM
|
10
|
+
remote: https://rubygems.org/
|
11
|
+
specs:
|
12
|
+
actioncable (6.0.2.1)
|
13
|
+
actionpack (= 6.0.2.1)
|
14
|
+
nio4r (~> 2.0)
|
15
|
+
websocket-driver (>= 0.6.1)
|
16
|
+
actionmailbox (6.0.2.1)
|
17
|
+
actionpack (= 6.0.2.1)
|
18
|
+
activejob (= 6.0.2.1)
|
19
|
+
activerecord (= 6.0.2.1)
|
20
|
+
activestorage (= 6.0.2.1)
|
21
|
+
activesupport (= 6.0.2.1)
|
22
|
+
mail (>= 2.7.1)
|
23
|
+
actionmailer (6.0.2.1)
|
24
|
+
actionpack (= 6.0.2.1)
|
25
|
+
actionview (= 6.0.2.1)
|
26
|
+
activejob (= 6.0.2.1)
|
27
|
+
mail (~> 2.5, >= 2.5.4)
|
28
|
+
rails-dom-testing (~> 2.0)
|
29
|
+
actionpack (6.0.2.1)
|
30
|
+
actionview (= 6.0.2.1)
|
31
|
+
activesupport (= 6.0.2.1)
|
32
|
+
rack (~> 2.0, >= 2.0.8)
|
33
|
+
rack-test (>= 0.6.3)
|
34
|
+
rails-dom-testing (~> 2.0)
|
35
|
+
rails-html-sanitizer (~> 1.0, >= 1.2.0)
|
36
|
+
actiontext (6.0.2.1)
|
37
|
+
actionpack (= 6.0.2.1)
|
38
|
+
activerecord (= 6.0.2.1)
|
39
|
+
activestorage (= 6.0.2.1)
|
40
|
+
activesupport (= 6.0.2.1)
|
41
|
+
nokogiri (>= 1.8.5)
|
42
|
+
actionview (6.0.2.1)
|
43
|
+
activesupport (= 6.0.2.1)
|
44
|
+
builder (~> 3.1)
|
45
|
+
erubi (~> 1.4)
|
46
|
+
rails-dom-testing (~> 2.0)
|
47
|
+
rails-html-sanitizer (~> 1.1, >= 1.2.0)
|
48
|
+
activejob (6.0.2.1)
|
49
|
+
activesupport (= 6.0.2.1)
|
50
|
+
globalid (>= 0.3.6)
|
51
|
+
activemodel (6.0.2.1)
|
52
|
+
activesupport (= 6.0.2.1)
|
53
|
+
activerecord (6.0.2.1)
|
54
|
+
activemodel (= 6.0.2.1)
|
55
|
+
activesupport (= 6.0.2.1)
|
56
|
+
activestorage (6.0.2.1)
|
57
|
+
actionpack (= 6.0.2.1)
|
58
|
+
activejob (= 6.0.2.1)
|
59
|
+
activerecord (= 6.0.2.1)
|
60
|
+
marcel (~> 0.3.1)
|
61
|
+
activesupport (6.0.2.1)
|
62
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
63
|
+
i18n (>= 0.7, < 2)
|
64
|
+
minitest (~> 5.1)
|
65
|
+
tzinfo (~> 1.1)
|
66
|
+
zeitwerk (~> 2.2)
|
67
|
+
aes (0.5.0)
|
68
|
+
binding_of_caller (0.8.0)
|
69
|
+
debug_inspector (>= 0.0.1)
|
70
|
+
builder (3.2.4)
|
71
|
+
codeclimate-test-reporter (1.0.7)
|
72
|
+
simplecov
|
73
|
+
coderay (1.1.2)
|
74
|
+
concurrent-ruby (1.1.6)
|
75
|
+
crass (1.0.6)
|
76
|
+
debug_inspector (0.0.3)
|
77
|
+
diff-lcs (1.3)
|
78
|
+
docile (1.3.2)
|
79
|
+
erubi (1.9.0)
|
80
|
+
globalid (0.4.2)
|
81
|
+
activesupport (>= 4.2.0)
|
82
|
+
i18n (1.8.2)
|
83
|
+
concurrent-ruby (~> 1.0)
|
84
|
+
ipaddr (1.2.2)
|
85
|
+
json (2.3.0)
|
86
|
+
loofah (2.4.0)
|
87
|
+
crass (~> 1.0.2)
|
88
|
+
nokogiri (>= 1.5.9)
|
89
|
+
mail (2.7.1)
|
90
|
+
mini_mime (>= 0.1.1)
|
91
|
+
marcel (0.3.3)
|
92
|
+
mimemagic (~> 0.3.2)
|
93
|
+
method_source (0.9.2)
|
94
|
+
mimemagic (0.3.4)
|
95
|
+
mini_mime (1.0.2)
|
96
|
+
mini_portile2 (2.4.0)
|
97
|
+
minitest (5.14.0)
|
98
|
+
nio4r (2.5.2)
|
99
|
+
nokogiri (1.10.9)
|
100
|
+
mini_portile2 (~> 2.4.0)
|
101
|
+
openssl (2.1.2)
|
102
|
+
ipaddr
|
103
|
+
pry (0.12.2)
|
104
|
+
coderay (~> 1.1.0)
|
105
|
+
method_source (~> 0.9.0)
|
106
|
+
pry-rails (0.3.9)
|
107
|
+
pry (>= 0.10.4)
|
108
|
+
pry-stack_explorer (0.4.9.3)
|
109
|
+
binding_of_caller (>= 0.7)
|
110
|
+
pry (>= 0.9.11)
|
111
|
+
rack (2.2.2)
|
112
|
+
rack-test (1.1.0)
|
113
|
+
rack (>= 1.0, < 3)
|
114
|
+
rails (6.0.2.1)
|
115
|
+
actioncable (= 6.0.2.1)
|
116
|
+
actionmailbox (= 6.0.2.1)
|
117
|
+
actionmailer (= 6.0.2.1)
|
118
|
+
actionpack (= 6.0.2.1)
|
119
|
+
actiontext (= 6.0.2.1)
|
120
|
+
actionview (= 6.0.2.1)
|
121
|
+
activejob (= 6.0.2.1)
|
122
|
+
activemodel (= 6.0.2.1)
|
123
|
+
activerecord (= 6.0.2.1)
|
124
|
+
activestorage (= 6.0.2.1)
|
125
|
+
activesupport (= 6.0.2.1)
|
126
|
+
bundler (>= 1.3.0)
|
127
|
+
railties (= 6.0.2.1)
|
128
|
+
sprockets-rails (>= 2.0.0)
|
129
|
+
rails-dom-testing (2.0.3)
|
130
|
+
activesupport (>= 4.2.0)
|
131
|
+
nokogiri (>= 1.6)
|
132
|
+
rails-html-sanitizer (1.3.0)
|
133
|
+
loofah (~> 2.3)
|
134
|
+
railties (6.0.2.1)
|
135
|
+
actionpack (= 6.0.2.1)
|
136
|
+
activesupport (= 6.0.2.1)
|
137
|
+
method_source
|
138
|
+
rake (>= 0.8.7)
|
139
|
+
thor (>= 0.20.3, < 2.0)
|
140
|
+
rake (13.0.1)
|
141
|
+
rspec (3.9.0)
|
142
|
+
rspec-core (~> 3.9.0)
|
143
|
+
rspec-expectations (~> 3.9.0)
|
144
|
+
rspec-mocks (~> 3.9.0)
|
145
|
+
rspec-core (3.9.1)
|
146
|
+
rspec-support (~> 3.9.1)
|
147
|
+
rspec-expectations (3.9.0)
|
148
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
149
|
+
rspec-support (~> 3.9.0)
|
150
|
+
rspec-mocks (3.9.1)
|
151
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
152
|
+
rspec-support (~> 3.9.0)
|
153
|
+
rspec-support (3.9.2)
|
154
|
+
simplecov (0.17.1)
|
155
|
+
docile (~> 1.1)
|
156
|
+
json (>= 1.8, < 3)
|
157
|
+
simplecov-html (~> 0.10.0)
|
158
|
+
simplecov-html (0.10.2)
|
159
|
+
sprockets (4.0.0)
|
160
|
+
concurrent-ruby (~> 1.0)
|
161
|
+
rack (> 1, < 3)
|
162
|
+
sprockets-rails (3.2.1)
|
163
|
+
actionpack (>= 4.0)
|
164
|
+
activesupport (>= 4.0)
|
165
|
+
sprockets (>= 3.0.0)
|
166
|
+
sqlite3 (1.4.2)
|
167
|
+
thor (1.0.1)
|
168
|
+
thread_safe (0.3.6)
|
169
|
+
tzinfo (1.2.6)
|
170
|
+
thread_safe (~> 0.1)
|
171
|
+
websocket-driver (0.7.1)
|
172
|
+
websocket-extensions (>= 0.1.0)
|
173
|
+
websocket-extensions (0.1.4)
|
174
|
+
zeitwerk (2.3.0)
|
175
|
+
|
176
|
+
PLATFORMS
|
177
|
+
ruby
|
178
|
+
|
179
|
+
DEPENDENCIES
|
180
|
+
bundler (> 1.17)
|
181
|
+
codeclimate-test-reporter
|
182
|
+
portunus!
|
183
|
+
pry-rails
|
184
|
+
pry-stack_explorer
|
185
|
+
rails (>= 5.0.0)
|
186
|
+
rake (> 12.3.3)
|
187
|
+
rspec
|
188
|
+
simplecov (~> 0.17.1)
|
189
|
+
sqlite3
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2019 Colin Petruno
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,289 @@
|
|
1
|
+
# Portunus
|
2
|
+
|
3
|
+
[](https://codeclimate.com/github/colinpetruno/portunus/maintainability) [](https://codeclimate.com/github/colinpetruno/portunus/test_coverage) [](https://travis-ci.org/colinpetruno/portunus)
|
4
|
+
|
5
|
+
Portunus is an opininated, object-oriented encryption engine built for Ruby on Rails
|
6
|
+
applications. It utilizes a KEK (Key Encryption Key) & DEK (Data
|
7
|
+
Encryption Key) scheme.
|
8
|
+
|
9
|
+
KEK keys should be stored outside the application. Portunus provides a
|
10
|
+
few default adaptors for working with common deployment setups. While this
|
11
|
+
is more secure than having unencrypted database data, the best use of
|
12
|
+
Portunus would connect to and do the encryption / decryption of your keys
|
13
|
+
inside of an HSM. Portunus is easily extensible to accomplish this but it's
|
14
|
+
not included due to the extensive variety of possible deployments.
|
15
|
+
|
16
|
+
Lastly, Portnus has scripts included to do automatic rotation of these keys.
|
17
|
+
It's important to rotate both master keys and data encryption keys. Scripts
|
18
|
+
are included for both of these that can be scheduled via cron.
|
19
|
+
|
20
|
+
## Background
|
21
|
+
Privacy and security need to be considered from the very start of building an
|
22
|
+
application. While web development has gotten more accessible, application
|
23
|
+
security has not. Portunus is intended to be a drop in utility that requires
|
24
|
+
just minutes of set up to ensure your app is using a DEK and KEK encryption
|
25
|
+
key to protect your database from several types of attacks. If you want to
|
26
|
+
go futher, it's easily extensible for your custom solution.
|
27
|
+
|
28
|
+
## Installation
|
29
|
+
|
30
|
+
### Install the gem
|
31
|
+
|
32
|
+
|
33
|
+
$ gem "portunus"
|
34
|
+
|
35
|
+
|
36
|
+
And then execute:
|
37
|
+
|
38
|
+
$ bundle
|
39
|
+
|
40
|
+
Or install it yourself as:
|
41
|
+
|
42
|
+
$ gem install portunus
|
43
|
+
|
44
|
+
Run the generator. This will create the required Portunus tables.
|
45
|
+
|
46
|
+
$ rails generate portunus:install
|
47
|
+
$ rails db:migrate
|
48
|
+
|
49
|
+
Include the encryptable module on any of your models or add it to
|
50
|
+
`ApplicationRecord` to ensure all your models have access to field encryption.
|
51
|
+
|
52
|
+
```ruby
|
53
|
+
include Portunus::Encryptable
|
54
|
+
```
|
55
|
+
|
56
|
+
### Set up your master keys
|
57
|
+
Portunus comes with two adaptors for your master keys, "credentials" and
|
58
|
+
"environment". This should cover the most common deploy scenarios. Before
|
59
|
+
Portunus can function, enabled master keys need to be added. There is a
|
60
|
+
generator to create the keys for you to then install in the proper
|
61
|
+
location.
|
62
|
+
|
63
|
+
$ bundle exec rake portunus:generate_master_keys
|
64
|
+
|
65
|
+
### Additional devise notes
|
66
|
+
|
67
|
+
There is additional configuration required if you are using devise and
|
68
|
+
desire to encrypt your email column. Devise will by default downcase email
|
69
|
+
addresses. The downcasing performed by devise happens after the portunus
|
70
|
+
encryption and result in broken encrypted values. This behaviour needs to
|
71
|
+
be disabled from devise and you will need to handle the downcasing prior
|
72
|
+
to encryption.
|
73
|
+
|
74
|
+
|
75
|
+
|
76
|
+
## Basic Configuration
|
77
|
+
|
78
|
+
To enable encryption on a column, add the `encrypted_fields` method in the
|
79
|
+
model and give it the fields you want to encrypt.
|
80
|
+
|
81
|
+
```ruby
|
82
|
+
class Member < ApplicationRecord
|
83
|
+
encrypted_fields :email
|
84
|
+
end
|
85
|
+
```
|
86
|
+
|
87
|
+
### Database level defaults
|
88
|
+
|
89
|
+
Since the database does not have access to your encryption engine, default
|
90
|
+
values will break the encryption. You need to ensure defaults for encrypted
|
91
|
+
columns are set within your application logic.
|
92
|
+
|
93
|
+
|
94
|
+
### Type casting
|
95
|
+
|
96
|
+
In order to provide a simpler implementation in your app, Portunus has type
|
97
|
+
casting support. The encrypted data must be stored as strings. To utilize
|
98
|
+
Portunus with different types, you may specify the type on the field.
|
99
|
+
|
100
|
+
```ruby
|
101
|
+
class User < ApplicationRecord
|
102
|
+
encrypted_fields :email, :firstname, birthdate: { type: :date }
|
103
|
+
end
|
104
|
+
```
|
105
|
+
|
106
|
+
#### Supported types
|
107
|
+
- Boolean (:boolean)
|
108
|
+
- Date (:date)
|
109
|
+
- DateTime (:datetime)
|
110
|
+
- Float (:float)
|
111
|
+
- Integer (:integer)
|
112
|
+
- String (:string) (default)
|
113
|
+
|
114
|
+
|
115
|
+
|
116
|
+
### Hashing
|
117
|
+
|
118
|
+
Encrypted data cannot be searched. Portunus provides an automatic hash
|
119
|
+
mechanism for encrypted data. The hashing happens prior to validation on the
|
120
|
+
model and will take your encrypted_field and put it into a column with a name
|
121
|
+
of `hashed_encrypted_field`.
|
122
|
+
|
123
|
+
For instance, a migration for this may look like.
|
124
|
+
|
125
|
+
```ruby
|
126
|
+
create_table :members do |t|
|
127
|
+
t.string :hashed_email, null: false
|
128
|
+
t.string :email, null: false
|
129
|
+
end
|
130
|
+
```
|
131
|
+
|
132
|
+
and the model:
|
133
|
+
|
134
|
+
```ruby
|
135
|
+
class Member < ApplicationRecord
|
136
|
+
encrypted_fields :email
|
137
|
+
end
|
138
|
+
```
|
139
|
+
|
140
|
+
#### Portunus::Hasher
|
141
|
+
There is a class provided to perform the hashing that you can utilize to look up date.
|
142
|
+
|
143
|
+
```ruby
|
144
|
+
User.find_by(email: ::Portunus::Hasher.for(params[:email])
|
145
|
+
```
|
146
|
+
|
147
|
+
## Advanced Setup
|
148
|
+
|
149
|
+
### Configuration block
|
150
|
+
Portunus can be easily customized using a configuration initializer.
|
151
|
+
|
152
|
+
```ruby
|
153
|
+
Portunus.configuration do |config|
|
154
|
+
config.storage_adaptor = Portunus::StorageAdaptors::Credentials
|
155
|
+
config.encrypter = Portunus::Encrypters::OpenSslAes
|
156
|
+
config.max_key_duration = 1.month
|
157
|
+
end
|
158
|
+
```
|
159
|
+
|
160
|
+
#### Options
|
161
|
+
- `storage_adaptor` - This is finds and looks up master keys.
|
162
|
+
- `encrypter` - This is responsible for setting the encrypter that encrypts
|
163
|
+
decrypts the data.
|
164
|
+
- `max_key_duration` - Timeframe for how old you want to allow keys to exist for.
|
165
|
+
Ideally your keys are constantly being rotated. Used in key rotation tasks.
|
166
|
+
|
167
|
+
|
168
|
+
## Storage adaptors
|
169
|
+
|
170
|
+
Storage adaptors provide the interface to determine which master key to decrypt
|
171
|
+
a data key. Portunus comes with two adaptors to access master keys out of the box.
|
172
|
+
|
173
|
+
- **Portunus::StorageAdaptors::Environment**
|
174
|
+
- **Portunus::StorageAdaptors::Credentials**
|
175
|
+
|
176
|
+
We need to keep track of the following items:
|
177
|
+
|
178
|
+
- **Key name** - This is what is stored on the data encryption key table to find the
|
179
|
+
master key
|
180
|
+
- **Enabled** - Whether the key is enabled for new data keys. Note: If you disable a
|
181
|
+
key, that just stops future keys from generating. Until all the keys are rotated,
|
182
|
+
do not remove the key.
|
183
|
+
- **Created date** - When the key was created to help track rotation duration
|
184
|
+
|
185
|
+
The master key id is stored on the data key table. These adaptors work like hash
|
186
|
+
maps. The key id is passed and a value is returned. The value for both default
|
187
|
+
adaptors is the master key. However if you were writing for an environment where keys
|
188
|
+
are stored inside an HSM the value could be the key id in the HSM. The encrypter
|
189
|
+
would then take that key id and interface with the HSM.
|
190
|
+
|
191
|
+
Adaptors are easily registered in the config so you can take an existing one
|
192
|
+
and customize to your requirements.
|
193
|
+
|
194
|
+
### EnvironmentAdaptor
|
195
|
+
Store and manage keys through any environment. Great for deployments like
|
196
|
+
Heroku. The environment adaptor needs multiple keys per master key to track
|
197
|
+
the key value, date created and enabled.
|
198
|
+
|
199
|
+
|
200
|
+
### Credentials adaptor (default)
|
201
|
+
This gets your master keys from your rails credential files. An example
|
202
|
+
structure is:
|
203
|
+
|
204
|
+
```yaml
|
205
|
+
portunus:
|
206
|
+
f9e59a8c17c5f430f17745a522ebc2b7:
|
207
|
+
key: 93a05a5ce18afb85162a34d552c953b3
|
208
|
+
enabled: true
|
209
|
+
created_at: "2020-03-13T12:11:11+01:00"
|
210
|
+
140f33e69f0647cbc14b64605f002ff6:
|
211
|
+
key: d2c2aa9b7aeff75513ca24efcd8b8dd3
|
212
|
+
enabled: true
|
213
|
+
created_at: "2020-03-13T12:11:11+01:00"
|
214
|
+
```
|
215
|
+
|
216
|
+
## Key rotation
|
217
|
+
Portunus provides key rotation scripts to rotate DEKs, KEKs, and both at
|
218
|
+
once. The DEK rotation script will rotate keys every six months. If provided
|
219
|
+
a force option as an environment variable it will rotate all the keys. The
|
220
|
+
KEK rotation will rotate all master keys. This will probably take a long time
|
221
|
+
in many apps so therefore you can rotate the master keys invidually by
|
222
|
+
providing the key name.
|
223
|
+
|
224
|
+
|
225
|
+
$ bundle exec rake portunus:rotate_deks
|
226
|
+
$ FORCE=true bundle exec rake portunus:rotate_deks
|
227
|
+
$ bundle exec rake portunus:rotate_keks
|
228
|
+
$ KEY_NAME=<keyname> bundle exec rake portunus:rotate_deks
|
229
|
+
|
230
|
+
|
231
|
+
## Tips
|
232
|
+
- Security is about applying layers. Using Portunus with the default
|
233
|
+
configuration helps protects against specific types of attacks. However, in the
|
234
|
+
event your complete environment is compromised there is not much that can
|
235
|
+
be done.
|
236
|
+
- Providing seperation of concerns within your organization can help if you
|
237
|
+
need the data to survive even if someone gets direct server access. Every
|
238
|
+
aspect of Portunus is easily configured to ensure this is possible for you.
|
239
|
+
- When deciding how many master keys to use, keep the amount of data in mind.
|
240
|
+
Each key is responsible for encrypting a certain number of DEKs. The lower
|
241
|
+
this is kept the easier it will be to rotate.
|
242
|
+
- Schedule rotation often. The dek rotator can be run every day or on even
|
243
|
+
smaller intervals.
|
244
|
+
|
245
|
+
|
246
|
+
|
247
|
+
|
248
|
+
|
249
|
+
## Improvements
|
250
|
+
|
251
|
+
Some items I'd like to see added:
|
252
|
+
|
253
|
+
- Migration support from an unencrypted to encrypted column
|
254
|
+
- Google Cloud HSM Encrypter
|
255
|
+
- Improve key rotations
|
256
|
+
- Research better devise solution.
|
257
|
+
- Different encrypters or key sources for different data rows
|
258
|
+
- Dashboard to show key usage and which keys can be removed
|
259
|
+
- Automatic master key introduction and rotation
|
260
|
+
|
261
|
+
|
262
|
+
## Development
|
263
|
+
|
264
|
+
After checking out the repo, run `bundle install to install dependencies.
|
265
|
+
|
266
|
+
To install this gem onto your local machine, run `bundle exec rake install`.
|
267
|
+
To release a new version, update the version number in `version.rb`, and then
|
268
|
+
run `bundle exec rake release`, which will create a git tag for the version,
|
269
|
+
push git commits and tags, and push the `.gem` file
|
270
|
+
to [rubygems.org](https://rubygems.org).
|
271
|
+
|
272
|
+
## Contributing
|
273
|
+
|
274
|
+
Bug reports and pull requests are welcome on GitHub at
|
275
|
+
https://github.com/colinpetruno/portunus. This project is intended to be a
|
276
|
+
safe, welcoming space for collaboration, and contributors are expected to
|
277
|
+
adhere to the [Contributor Covenant](http://contributor-covenant.org) code of
|
278
|
+
conduct.
|
279
|
+
|
280
|
+
## License
|
281
|
+
|
282
|
+
The gem is available as open source under the terms of
|
283
|
+
the [MIT License](https://opensource.org/licenses/MIT).
|
284
|
+
|
285
|
+
## Code of Conduct
|
286
|
+
|
287
|
+
Everyone interacting in the Portunus project’s codebases, issue trackers,
|
288
|
+
chat rooms and mailing lists is expected to follow
|
289
|
+
the [code of conduct](https://github.com/[USERNAME]/portunus/blob/master/CODE_OF_CONDUCT.md).
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "portunus"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
data/lib/Rakefile
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'rails/generators'
|
2
|
+
require 'rails/generators/migration'
|
3
|
+
require "rails/generators/active_record/migration"
|
4
|
+
|
5
|
+
module Portunus
|
6
|
+
module Generators
|
7
|
+
class InstallGenerator < Rails::Generators::Base
|
8
|
+
include ActiveRecord::Generators::Migration
|
9
|
+
|
10
|
+
source_root File.expand_path("templates", __dir__)
|
11
|
+
desc "Add the migrations for Porteus"
|
12
|
+
|
13
|
+
def copy_migrations
|
14
|
+
warn "Creating Migrations for Portunus Encryption"
|
15
|
+
migration_template(
|
16
|
+
"create_portunus.rb.erb",
|
17
|
+
"db/migrate/create_portunus.rb"
|
18
|
+
)
|
19
|
+
end
|
20
|
+
|
21
|
+
def migration_version
|
22
|
+
"[#{Rails::VERSION::MAJOR}.#{Rails::VERSION::MINOR}]"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|