certmeister 0.0.2 → 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/Gemfile.lock +3 -3
- data/README.md +18 -2
- data/contrib/.ruby-gemset +1 -0
- data/contrib/.ruby-version +1 -0
- data/contrib/Gemfile +6 -0
- data/contrib/Gemfile.lock +24 -0
- data/contrib/config.ru +77 -0
- data/contrib/hosts +3 -0
- data/contrib/redis.yml +14 -0
- data/lib/certmeister/base.rb +12 -3
- data/lib/certmeister/response.rb +19 -11
- data/lib/certmeister/version.rb +1 -1
- data/spec/certmeister/base_spec.rb +1 -1
- data/spec/certmeister/response_spec.rb +31 -6
- metadata +9 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 45294a93b1babeea90937ee91c345bddefa60b25
|
4
|
+
data.tar.gz: 1c0f12426788ad03d9952e2e3532128951d9575e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 763ed829a24c1002c00e3732b41db17d013195ad61686f6552d07424ed4ba101f5ff18545c47a1caa50c81228cb46961dac32048227b2ec9fcc164b69aab91cb
|
7
|
+
data.tar.gz: a953b468dddfdf2a27f381b1c9b316926ee7397011b9e28cbb9fd9834cfd439073ccf7a8c52fdeb98985785479ebe4b510f0ead3a791872aa31128239a05d463
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -26,7 +26,23 @@ To hit the service:
|
|
26
26
|
```
|
27
27
|
$ curl -L \
|
28
28
|
-d "psk=secretkey" \
|
29
|
-
-d "csr=$(perl -MURI::Escape -e 'print uri_escape(join("", <STDIN>));' <
|
30
|
-
http://certmeister.hetzner.co.za/certificate
|
29
|
+
-d "csr=$(perl -MURI::Escape -e 'print uri_escape(join("", <STDIN>));' < fixtures/client.csr)" \
|
30
|
+
http://certmeister.hetzner.co.za/certificate/axl.starjuice.net
|
31
31
|
```
|
32
32
|
|
33
|
+
## Testing
|
34
|
+
|
35
|
+
Because we test both certmeister and certmeister-redis with `rake spec`, you need redis up if you want to run the tests. It's easy:
|
36
|
+
|
37
|
+
* Install redis-2.8.4 or later.
|
38
|
+
* Start redis.
|
39
|
+
* Run tests.
|
40
|
+
* Stop redis.
|
41
|
+
|
42
|
+
```
|
43
|
+
sudo yum install -y ansible
|
44
|
+
sudo ansible-playbook -i contrib/hosts contrib/redis.yml
|
45
|
+
redis-server --logfile /dev/null &
|
46
|
+
rake spec
|
47
|
+
kill %1; wait %1
|
48
|
+
```
|
@@ -0,0 +1 @@
|
|
1
|
+
certmeister-contrib
|
@@ -0,0 +1 @@
|
|
1
|
+
ruby-2.0.0-p247
|
data/contrib/Gemfile
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
PATH
|
2
|
+
remote: ..
|
3
|
+
specs:
|
4
|
+
certmeister (0.0.2)
|
5
|
+
certmeister-redis (0.0.2)
|
6
|
+
certmeister (= 0.0.2)
|
7
|
+
redis-sentinel (~> 1.4)
|
8
|
+
|
9
|
+
GEM
|
10
|
+
remote: https://rubygems.org/
|
11
|
+
specs:
|
12
|
+
rack (1.5.2)
|
13
|
+
redis (3.0.7)
|
14
|
+
redis-sentinel (1.4.2)
|
15
|
+
redis
|
16
|
+
|
17
|
+
PLATFORMS
|
18
|
+
ruby
|
19
|
+
|
20
|
+
DEPENDENCIES
|
21
|
+
certmeister!
|
22
|
+
certmeister-redis!
|
23
|
+
rack
|
24
|
+
redis
|
data/contrib/config.ru
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rack'
|
3
|
+
|
4
|
+
require 'certmeister'
|
5
|
+
require 'certmeister/redis/store'
|
6
|
+
require 'redis'
|
7
|
+
|
8
|
+
allow = Certmeister::Policy::Noop.new
|
9
|
+
|
10
|
+
ca = Certmeister.new(
|
11
|
+
Certmeister::Config.new(
|
12
|
+
sign_policy: allow,
|
13
|
+
fetch_policy: allow,
|
14
|
+
remove_policy: allow,
|
15
|
+
store: Certmeister::Redis::Store.new(Redis.new, "development"),
|
16
|
+
ca_cert: File.read("../fixtures/ca.crt"),
|
17
|
+
ca_key: File.read("../fixtures/ca.key"),
|
18
|
+
)
|
19
|
+
)
|
20
|
+
|
21
|
+
sign_action = ->(params) do
|
22
|
+
response = ca.sign(params)
|
23
|
+
if response.error?
|
24
|
+
[500, {'Content-Type' => 'text/plain'}, ["500 Internal Server Error (#{response.error})"]]
|
25
|
+
elsif response.denied?
|
26
|
+
[403, {'Content-Type' => 'text/plain'}, ["403 Forbidden (#{response.error})"]]
|
27
|
+
else
|
28
|
+
[303, {'Content-Type' => 'text/plain',
|
29
|
+
'Location' => "/certificate/#{params[:cn]}"}, ["303 See Other"]]
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
fetch_action = ->(params) do
|
34
|
+
response = ca.fetch(params)
|
35
|
+
if response.error?
|
36
|
+
[500, {'Content-Type' => 'text/plain'}, ["500 Internal Server Error (#{response.error})"]]
|
37
|
+
elsif response.denied?
|
38
|
+
[403, {'Content-Type' => 'text/plain'}, ["403 Forbidden (#{response.error})"]]
|
39
|
+
elsif response.miss?
|
40
|
+
[404, {'Content-Type' => 'text/plain'}, ["404 Not Found"]]
|
41
|
+
else
|
42
|
+
[200, {'Content-Type' => 'application/x-pem-file'}, [response.pem]]
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
remove_action = ->(params) do
|
47
|
+
response = ca.remove(params)
|
48
|
+
if response.error?
|
49
|
+
[500, {'Content-Type' => 'text/plain'}, ["500 Internal Server Error (#{response.error})"]]
|
50
|
+
elsif response.denied?
|
51
|
+
[403, {'Content-Type' => 'text/plain'}, ["403 Forbidden (#{response.error})"]]
|
52
|
+
elsif response.miss?
|
53
|
+
[404, {'Content-Type' => 'text/plain'}, ["404 Not Found"]]
|
54
|
+
else
|
55
|
+
[200, {'Content-Type' => 'text/plain'}, ["200 OK"]]
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
router = ->(env) do
|
60
|
+
req = Rack::Request.new(env)
|
61
|
+
if req.path_info =~ /^\/certificate\/(.+)/
|
62
|
+
params = req.params.tap do |p|
|
63
|
+
p[:cn] = $1
|
64
|
+
p[:ip] = req.ip
|
65
|
+
end
|
66
|
+
case req.request_method
|
67
|
+
when 'POST' then sign_action.call(params)
|
68
|
+
when 'GET' then fetch_action.call(params)
|
69
|
+
when 'DELETE' then remove_action.call(params)
|
70
|
+
else [405, {'Content-Type' => 'text-plain'}, ["405 Method Not Allowed"]]
|
71
|
+
end
|
72
|
+
else
|
73
|
+
[501, {'Content-Type' => 'text-plain'}, ["501 Not Implemented"]]
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
run router
|
data/contrib/hosts
ADDED
data/contrib/redis.yml
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
---
|
2
|
+
- hosts: local
|
3
|
+
connection: local
|
4
|
+
vars:
|
5
|
+
version: 2.8.4
|
6
|
+
tasks:
|
7
|
+
- name: Download redis source
|
8
|
+
get_url: dest=/usr/src/redis-{{version}}.tar.gz url=http://download.redis.io/releases/redis-{{version}}.tar.gz
|
9
|
+
- name: Unpack redis source
|
10
|
+
command: tar -C /usr/src -xzf /usr/src/redis-{{version}}.tar.gz creates=/usr/src/redis-{{version}}
|
11
|
+
- name: Build redis from source
|
12
|
+
command: chdir=/usr/src/redis-{{version}} make creates=/usr/src/redis-{{version}}/src/redis-server
|
13
|
+
- name: Install redis from source
|
14
|
+
command: chdir=/usr/src/redis-{{version}} make install creates=/usr/local/bin/redis-server
|
data/lib/certmeister/base.rb
CHANGED
@@ -15,7 +15,8 @@ module Certmeister
|
|
15
15
|
@store = config.store
|
16
16
|
@openssl_digest = config.openssl_digest
|
17
17
|
else
|
18
|
-
|
18
|
+
reasons = config.errors.map { |kv| kv.join(' ') }
|
19
|
+
raise RuntimeError.new("invalid config: #{reasons.join('; ')}")
|
19
20
|
end
|
20
21
|
end
|
21
22
|
|
@@ -39,13 +40,21 @@ module Certmeister
|
|
39
40
|
|
40
41
|
def fetch(request)
|
41
42
|
subject_to_policy(@fetch_policy, request) do |request|
|
42
|
-
|
43
|
+
if pem = @store.fetch(request[:cn])
|
44
|
+
Certmeister::Response.hit(pem)
|
45
|
+
else
|
46
|
+
Certmeister::Response.miss
|
47
|
+
end
|
43
48
|
end
|
44
49
|
end
|
45
50
|
|
46
51
|
def remove(request)
|
47
52
|
subject_to_policy(@remove_policy, request) do |request|
|
48
|
-
|
53
|
+
if @store.remove(request[:cn])
|
54
|
+
Certmeister::Response.hit
|
55
|
+
else
|
56
|
+
Certmeister::Response.miss
|
57
|
+
end
|
49
58
|
end
|
50
59
|
end
|
51
60
|
|
data/lib/certmeister/response.rb
CHANGED
@@ -2,12 +2,12 @@ module Certmeister
|
|
2
2
|
|
3
3
|
class Response
|
4
4
|
|
5
|
-
|
5
|
+
private_class_method :new
|
6
|
+
|
7
|
+
def initialize(type, pem, error)
|
8
|
+
@type = type
|
6
9
|
@pem = pem
|
7
10
|
@error = error
|
8
|
-
if @pem and @error
|
9
|
-
raise ArgumentError.new("pem and error are mutually exclusive")
|
10
|
-
end
|
11
11
|
end
|
12
12
|
|
13
13
|
def pem
|
@@ -19,27 +19,35 @@ module Certmeister
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def hit?
|
22
|
-
|
22
|
+
@type == :hit
|
23
23
|
end
|
24
24
|
|
25
25
|
def miss?
|
26
|
-
|
26
|
+
@type == :miss
|
27
|
+
end
|
28
|
+
|
29
|
+
def denied?
|
30
|
+
@type == :denied
|
27
31
|
end
|
28
32
|
|
29
33
|
def error?
|
30
|
-
|
34
|
+
@type == :error
|
31
35
|
end
|
32
36
|
|
33
|
-
def self.hit(pem)
|
34
|
-
|
37
|
+
def self.hit(pem = :none)
|
38
|
+
new(:hit, pem, nil)
|
35
39
|
end
|
36
40
|
|
37
41
|
def self.miss
|
38
|
-
|
42
|
+
new(:miss, nil, nil)
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.denied(message)
|
46
|
+
new(:denied, nil, message)
|
39
47
|
end
|
40
48
|
|
41
49
|
def self.error(message)
|
42
|
-
|
50
|
+
new(:error, nil, message)
|
43
51
|
end
|
44
52
|
|
45
53
|
end
|
data/lib/certmeister/version.rb
CHANGED
@@ -44,7 +44,7 @@ describe Certmeister do
|
|
44
44
|
expect(response.error).to eql "invalid CSR (not enough data)"
|
45
45
|
end
|
46
46
|
|
47
|
-
it "refuses to sign a CSR if the subject does not agree with the request CN" do
|
47
|
+
it "(XXX move this into the policy) refuses to sign a CSR if the subject does not agree with the request CN" do
|
48
48
|
request = valid_request.tap { |r| r[:cn] = "monkeyface.example.com" }
|
49
49
|
ca = Certmeister.new(CertmeisterConfigHelper::valid_config)
|
50
50
|
response = ca.sign(request)
|
@@ -6,13 +6,13 @@ describe Certmeister::Response do
|
|
6
6
|
|
7
7
|
let(:pem) { File.read('fixtures/client.crt') }
|
8
8
|
|
9
|
-
it "
|
10
|
-
expect { Certmeister::Response.new(pem,
|
9
|
+
it "must be instantiated via the factory methods only" do
|
10
|
+
expect { Certmeister::Response.new(pem, nil, nil) }.to raise_error(NoMethodError, /private/)
|
11
11
|
end
|
12
12
|
|
13
13
|
describe "on error" do
|
14
14
|
|
15
|
-
subject { Certmeister::Response.
|
15
|
+
subject { Certmeister::Response.error("something went wrong") }
|
16
16
|
|
17
17
|
it "provides the error" do
|
18
18
|
expect(subject.error).to eql "something went wrong"
|
@@ -25,14 +25,36 @@ describe Certmeister::Response do
|
|
25
25
|
it "offers appropriate boolean flags" do
|
26
26
|
expect(subject.hit?).to be_false
|
27
27
|
expect(subject.miss?).to be_false
|
28
|
+
expect(subject.denied?).to be_false
|
28
29
|
expect(subject.error?).to be_true
|
29
30
|
end
|
30
31
|
|
31
32
|
end
|
32
33
|
|
34
|
+
describe "on denial" do
|
35
|
+
|
36
|
+
subject { Certmeister::Response.denied("bad client, no cookie") }
|
37
|
+
|
38
|
+
it "provides the reason" do
|
39
|
+
expect(subject.error).to eql "bad client, no cookie"
|
40
|
+
end
|
41
|
+
|
42
|
+
it "doesn't provide the PEM-encoded X509 certificate as a string" do
|
43
|
+
expect(subject.pem).to be_nil
|
44
|
+
end
|
45
|
+
|
46
|
+
it "offers appropriate boolean flags" do
|
47
|
+
expect(subject.hit?).to be_false
|
48
|
+
expect(subject.miss?).to be_false
|
49
|
+
expect(subject.denied?).to be_true
|
50
|
+
expect(subject.error?).to be_false
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
33
55
|
describe "on miss (i.e. not found)" do
|
34
56
|
|
35
|
-
subject { Certmeister::Response.
|
57
|
+
subject { Certmeister::Response.miss }
|
36
58
|
|
37
59
|
it "has no error" do
|
38
60
|
expect(subject.error).to be_nil
|
@@ -45,6 +67,7 @@ describe Certmeister::Response do
|
|
45
67
|
it "offers appropriate boolean flags" do
|
46
68
|
expect(subject.hit?).to be_false
|
47
69
|
expect(subject.miss?).to be_true
|
70
|
+
expect(subject.denied?).to be_false
|
48
71
|
expect(subject.error?).to be_false
|
49
72
|
end
|
50
73
|
|
@@ -52,19 +75,21 @@ describe Certmeister::Response do
|
|
52
75
|
|
53
76
|
describe "on hit (success)" do
|
54
77
|
|
55
|
-
subject { Certmeister::Response.
|
78
|
+
subject { Certmeister::Response.hit(pem) }
|
56
79
|
|
57
80
|
it "has no error" do
|
58
81
|
expect(subject.error).to be_nil
|
59
82
|
end
|
60
83
|
|
61
|
-
it "provides the PEM-encoded X509 certificate as a string" do
|
84
|
+
it "provides the PEM-encoded X509 certificate as a string (or :none)" do
|
85
|
+
# some hits, like the one for a remove(), don't have a pem, returning :none
|
62
86
|
expect(subject.pem).to eql pem
|
63
87
|
end
|
64
88
|
|
65
89
|
it "offers appropriate boolean flags" do
|
66
90
|
expect(subject.hit?).to be_true
|
67
91
|
expect(subject.miss?).to be_false
|
92
|
+
expect(subject.denied?).to be_false
|
68
93
|
expect(subject.error?).to be_false
|
69
94
|
end
|
70
95
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: certmeister
|
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
|
- Sheldon Hearn
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-01-
|
11
|
+
date: 2014-01-31 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -73,6 +73,13 @@ files:
|
|
73
73
|
- Rakefile
|
74
74
|
- certmeister-redis.gemspec
|
75
75
|
- certmeister.gemspec
|
76
|
+
- contrib/.ruby-gemset
|
77
|
+
- contrib/.ruby-version
|
78
|
+
- contrib/Gemfile
|
79
|
+
- contrib/Gemfile.lock
|
80
|
+
- contrib/config.ru
|
81
|
+
- contrib/hosts
|
82
|
+
- contrib/redis.yml
|
76
83
|
- fixtures/ca.crt
|
77
84
|
- fixtures/ca.csr
|
78
85
|
- fixtures/ca.key
|