certmeister 0.0.2 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|