sidekiq-encryptor 0.0.1 → 0.1.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 +4 -4
- data/.pryrc +1 -1
- data/.travis.yml +1 -3
- data/CHANGELOG.md +28 -0
- data/README.md +10 -1
- data/lib/sidekiq/encryptor/version.rb +4 -2
- data/lib/sidekiq/encryptor.rb +107 -13
- data/sidekiq-encryptor.gemspec +2 -1
- data/spec/sidekiq/encryptor_spec.rb +45 -17
- data/spec/spec_helper.rb +1 -1
- metadata +14 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cc401443775158ce8894d1474e2e68d9f72e0e3d
|
4
|
+
data.tar.gz: ad006a7f9a3b5969b524ac67e13b8e5b3657290f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6d8c9e5439e8223dc1c03ab4b7565520cad982f2707f8971dff14de3c0e748efa0874571c611a2381f1c66c124697ec499c85b432467837a6ffdc24c2a46397f
|
7
|
+
data.tar.gz: 1516b7eb16752e7526f4298ba6250cd104033ad777e1cb9edf7b92e8c8fa9d897319a5c8163269c3a3dbb2a665cb020dd166310e59c11986e6666a016d64f81c
|
data/.pryrc
CHANGED
data/.travis.yml
CHANGED
data/CHANGELOG.md
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# sidekiq-encryptor changelog
|
2
|
+
|
3
|
+
## 0.1.0
|
4
|
+
|
5
|
+
Backwards incompatible changes:
|
6
|
+
|
7
|
+
* Protocol version change from 0 to 1. Jobs encrypted by previous
|
8
|
+
versions cannot be decrypted by this version.
|
9
|
+
|
10
|
+
Changes:
|
11
|
+
|
12
|
+
* Switch to Fernet 2.0
|
13
|
+
* Use a distinct protocol version number
|
14
|
+
* Support binary, hexadecimal, and base64 keys. Note that many ASCII
|
15
|
+
keys will be detected as base64. Base64 assumes RFC 2045.
|
16
|
+
* Enforce 256 bit key length
|
17
|
+
* Recommend 32 byte keys in README
|
18
|
+
* Allow an optional `adapter` to be passed in. The adapter must follow
|
19
|
+
the following standards:
|
20
|
+
* Define a `encrypt` and `decrypt` class methods that accept two
|
21
|
+
arguments: `String key, String data`. `key` will always have length
|
22
|
+
32, `data` will be of arbitrary length.
|
23
|
+
* `encrypt` must return a String.
|
24
|
+
* `decrypt` must return a String or nil if the decryption fails.
|
25
|
+
|
26
|
+
## 0.0.1
|
27
|
+
|
28
|
+
Initial release
|
data/README.md
CHANGED
@@ -48,9 +48,18 @@ Sidekiq.configure_client do |config|
|
|
48
48
|
end
|
49
49
|
```
|
50
50
|
|
51
|
+
You should also set `SIDEKIQ_ENCRYPTION_KEY` to something sufficiently
|
52
|
+
random. The `openssl` tool is a good choice for this:
|
53
|
+
|
54
|
+
```sh
|
55
|
+
echo SIDEKIQ_ENCRYPTION_KEY=$(openssl rand -base64 32) >>.env
|
56
|
+
heroku config:set SIDEKIQ_ENCRYPTION_KEY=$(openssl rand -base64 32)
|
57
|
+
```
|
58
|
+
|
51
59
|
## Contributing
|
52
60
|
|
53
|
-
Pull requests gladly accepted. Please write tests for any code changes
|
61
|
+
Pull requests gladly accepted. Please write tests for any code changes,
|
62
|
+
though I'll admit my test coverage is completely awful right now.
|
54
63
|
|
55
64
|
## License
|
56
65
|
|
data/lib/sidekiq/encryptor.rb
CHANGED
@@ -1,7 +1,11 @@
|
|
1
|
+
# stdlib
|
2
|
+
require 'base64'
|
3
|
+
|
4
|
+
# gems
|
1
5
|
require 'sidekiq'
|
2
|
-
require '
|
3
|
-
require 'active_support/message_verifier'
|
6
|
+
require 'fernet'
|
4
7
|
|
8
|
+
# local files
|
5
9
|
require 'sidekiq/encryptor/version'
|
6
10
|
|
7
11
|
module Sidekiq
|
@@ -11,35 +15,125 @@ module Sidekiq
|
|
11
15
|
DecryptionError = Class.new(Error)
|
12
16
|
VersionChangeError = Class.new(DecryptionError)
|
13
17
|
|
18
|
+
module FernetAdapter
|
19
|
+
def self.encrypt(key, data)
|
20
|
+
Fernet.generate(Base64.urlsafe_encode64(key), data)
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.decrypt(key, data)
|
24
|
+
verifier = Fernet::Verifier.new(
|
25
|
+
token: data,
|
26
|
+
secret: Base64.urlsafe_encode64(key),
|
27
|
+
enforce_ttl: false)
|
28
|
+
verifier.valid? ? verifier.message : nil
|
29
|
+
rescue OpenSSL::Cipher::CipherError
|
30
|
+
nil
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
14
34
|
class Base
|
15
35
|
def initialize(options = {})
|
16
|
-
@key = options[:key]
|
17
|
-
@
|
36
|
+
@key = validate_key(compact_key(options[:key]))
|
37
|
+
@adapter = options[:adapter] || FernetAdapter
|
38
|
+
end
|
39
|
+
|
40
|
+
def inspect
|
41
|
+
"#<#{self.class.inspect}> @key=[masked] @adapter=#{@adapter.inspect}>"
|
42
|
+
end
|
43
|
+
alias to_s inspect
|
44
|
+
|
45
|
+
def enabled?
|
46
|
+
!@key.nil?
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def compact_key(key)
|
52
|
+
flat_key = key.to_s.delete("\r\n")
|
53
|
+
case flat_key
|
54
|
+
# empty
|
55
|
+
when ""
|
56
|
+
nil
|
57
|
+
# hexadecimal
|
58
|
+
when /^[\da-f]+$/i
|
59
|
+
[flat_key].pack('H*')
|
60
|
+
# base64
|
61
|
+
when /^[A-Za-z\d\+\/=]+$/
|
62
|
+
key.unpack('m*').first
|
63
|
+
# assume binary otherwise
|
64
|
+
else
|
65
|
+
key
|
66
|
+
end
|
18
67
|
end
|
68
|
+
|
69
|
+
def validate_key(key)
|
70
|
+
if key.nil?
|
71
|
+
$stderr.puts '[sidekiq-encryptor] ERROR: no key provided, encryption disabled'
|
72
|
+
elsif key.length < 32
|
73
|
+
$stderr.puts '[sidekiq-encryptor] ERROR: key length less than 256 bits, encryption disabled'
|
74
|
+
else
|
75
|
+
key[0,32]
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
19
79
|
end
|
20
80
|
|
21
81
|
class Client < Base
|
22
82
|
def call(worker, msg, queue)
|
23
|
-
return yield unless
|
24
|
-
msg['args'] =
|
83
|
+
return yield unless enabled?
|
84
|
+
msg['args'] = payload(msg['args'])
|
25
85
|
yield
|
26
86
|
end
|
87
|
+
|
88
|
+
private
|
89
|
+
|
90
|
+
def payload(input)
|
91
|
+
[
|
92
|
+
'Sidekiq::Encryptor',
|
93
|
+
Sidekiq::Encryptor::PROTOCOL_VERSION,
|
94
|
+
encrypt(input)
|
95
|
+
]
|
96
|
+
end
|
97
|
+
|
98
|
+
def encrypt(input)
|
99
|
+
@adapter.encrypt(@key, Sidekiq.dump_json(input))
|
100
|
+
end
|
101
|
+
|
27
102
|
end
|
28
103
|
|
29
104
|
class Server < Base
|
30
105
|
def call(worker, msg, queue)
|
31
|
-
return yield unless
|
32
|
-
|
33
|
-
|
106
|
+
return yield unless enabled?
|
107
|
+
msg['args'] = validate_and_decrypt(msg['args'])
|
108
|
+
yield
|
109
|
+
end
|
110
|
+
|
111
|
+
private
|
112
|
+
|
113
|
+
def validate_and_decrypt(payload)
|
114
|
+
if encrypted?(payload)
|
115
|
+
if version_changed?(payload)
|
34
116
|
raise VersionChangeError, 'incompatible change detected'
|
35
117
|
else
|
36
|
-
|
118
|
+
data = decrypt(payload) or
|
119
|
+
raise DecryptionError, 'key not identical or data was corrupted'
|
120
|
+
Sidekiq.load_json(data)
|
37
121
|
end
|
38
122
|
end
|
39
|
-
yield
|
40
|
-
rescue ActiveSupport::MessageVerifier::InvalidSignature, ActiveSupport::MessageEncryptor::InvalidMessage
|
41
|
-
raise DecryptionError, 'key not identical or data was corrupted'
|
42
123
|
end
|
124
|
+
|
125
|
+
def encrypted?(input)
|
126
|
+
input.is_a?(Array) && input.size == 3 && input.first == 'Sidekiq::Encryptor'
|
127
|
+
end
|
128
|
+
|
129
|
+
def version_changed?(input)
|
130
|
+
input[1] != Sidekiq::Encryptor::PROTOCOL_VERSION
|
131
|
+
end
|
132
|
+
|
133
|
+
def decrypt(input)
|
134
|
+
@adapter.decrypt(@key, input[2])
|
135
|
+
end
|
136
|
+
|
43
137
|
end
|
44
138
|
|
45
139
|
end # Encryptor
|
data/sidekiq-encryptor.gemspec
CHANGED
@@ -11,13 +11,14 @@ Gem::Specification.new do |gem|
|
|
11
11
|
gem.description = %q{Sidekiq middleware that encrypts your job data into and out of Redis.}
|
12
12
|
gem.summary = %q{Sidekiq::Encryptor is a middleware for Sidekiq that keeps your information safe by using 2-way encryption when storing and retrieving jobs from Redis.}
|
13
13
|
gem.homepage = 'https://github.com/wuputah/sidekiq-encryptor'
|
14
|
+
gem.license = 'MIT'
|
14
15
|
|
15
16
|
gem.files = `git ls-files`.split($/)
|
16
17
|
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
17
18
|
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
19
|
gem.require_paths = ['lib']
|
19
20
|
|
20
|
-
gem.add_dependency '
|
21
|
+
gem.add_dependency 'fernet', '>= 2.0rc1', '< 3.0'
|
21
22
|
gem.add_dependency 'sidekiq', '>= 2.5', '< 3.0'
|
22
23
|
|
23
24
|
gem.add_development_dependency 'rake'
|
@@ -1,31 +1,59 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
+
require 'securerandom'
|
2
3
|
|
3
4
|
[Sidekiq::Encryptor::Client, Sidekiq::Encryptor::Server].each do |klass|
|
4
5
|
|
5
6
|
describe klass do
|
6
7
|
|
7
|
-
|
8
|
-
described_class.new
|
9
|
-
end
|
8
|
+
raw_key = SecureRandom.random_bytes(32)
|
10
9
|
|
11
|
-
|
12
|
-
|
13
|
-
|
10
|
+
{
|
11
|
+
'base64' => [raw_key].pack('m*'),
|
12
|
+
'hex' => raw_key.unpack('H*').first,
|
13
|
+
'binary' => raw_key
|
14
|
+
}.each_pair do |key_type, key|
|
14
15
|
|
15
|
-
|
16
|
-
{
|
17
|
-
args: 'Clint Eastwood'
|
18
|
-
}
|
19
|
-
end
|
16
|
+
describe "with #{key_type} key" do
|
20
17
|
|
21
|
-
|
22
|
-
|
23
|
-
|
18
|
+
subject(:middleware) do
|
19
|
+
described_class.new(key: key)
|
20
|
+
end
|
21
|
+
|
22
|
+
let(:worker) do
|
23
|
+
RegularWorker.new
|
24
|
+
end
|
25
|
+
|
26
|
+
let(:data) do
|
27
|
+
['Clint Eastwood']
|
28
|
+
end
|
29
|
+
|
30
|
+
let(:args) do
|
31
|
+
{
|
32
|
+
Sidekiq::Encryptor::Client => data,
|
33
|
+
Sidekiq::Encryptor::Server => [
|
34
|
+
'Sidekiq::Encryptor',
|
35
|
+
1,
|
36
|
+
Fernet.generate(Base64.urlsafe_encode64(raw_key), JSON.dump(data))
|
37
|
+
]
|
38
|
+
}
|
39
|
+
end
|
40
|
+
|
41
|
+
let(:message) do
|
42
|
+
{ 'args' => args[klass] }
|
43
|
+
end
|
44
|
+
|
45
|
+
let(:queue) do
|
46
|
+
'default'
|
47
|
+
end
|
48
|
+
|
49
|
+
it { should be_enabled }
|
24
50
|
|
25
|
-
|
51
|
+
describe '#call' do
|
26
52
|
|
27
|
-
|
28
|
-
|
53
|
+
it 'yields' do
|
54
|
+
expect { |b| middleware.call(worker, message, queue, &b) }.to yield_with_no_args
|
55
|
+
end
|
56
|
+
end
|
29
57
|
end
|
30
58
|
end
|
31
59
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -19,7 +19,7 @@ require 'sidekiq/encryptor'
|
|
19
19
|
# Autoload every worker for the test suite that sits in spec/app/workers
|
20
20
|
Dir[File.join(WORKERS, '*.rb')].sort.each do |file|
|
21
21
|
name = File.basename(file, '.rb')
|
22
|
-
|
22
|
+
require name
|
23
23
|
end
|
24
24
|
|
25
25
|
RSpec.configure do |config|
|
metadata
CHANGED
@@ -1,29 +1,35 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sidekiq-encryptor
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jonathan Dance
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-08-
|
11
|
+
date: 2013-08-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: fernet
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
17
|
- - '>='
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
19
|
+
version: 2.0rc1
|
20
|
+
- - <
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '3.0'
|
20
23
|
type: :runtime
|
21
24
|
prerelease: false
|
22
25
|
version_requirements: !ruby/object:Gem::Requirement
|
23
26
|
requirements:
|
24
27
|
- - '>='
|
25
28
|
- !ruby/object:Gem::Version
|
26
|
-
version:
|
29
|
+
version: 2.0rc1
|
30
|
+
- - <
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '3.0'
|
27
33
|
- !ruby/object:Gem::Dependency
|
28
34
|
name: sidekiq
|
29
35
|
requirement: !ruby/object:Gem::Requirement
|
@@ -265,6 +271,7 @@ files:
|
|
265
271
|
- .pryrc
|
266
272
|
- .travis.yml
|
267
273
|
- .yardopts
|
274
|
+
- CHANGELOG.md
|
268
275
|
- Gemfile
|
269
276
|
- Guardfile
|
270
277
|
- LICENSE.txt
|
@@ -280,7 +287,8 @@ files:
|
|
280
287
|
- spec/spec_helper.rb
|
281
288
|
- spec/support/sidekiq.rb
|
282
289
|
homepage: https://github.com/wuputah/sidekiq-encryptor
|
283
|
-
licenses:
|
290
|
+
licenses:
|
291
|
+
- MIT
|
284
292
|
metadata: {}
|
285
293
|
post_install_message:
|
286
294
|
rdoc_options: []
|