certmeister 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +20 -0
  3. data/.rspec +2 -0
  4. data/.ruby-gemset +1 -0
  5. data/.ruby-version +1 -0
  6. data/Gemfile +4 -0
  7. data/Gemfile.lock +27 -0
  8. data/LICENSE +20 -0
  9. data/README.md +32 -0
  10. data/Rakefile +6 -0
  11. data/certmeister.gemspec +26 -0
  12. data/fixtures/ca.crt +15 -0
  13. data/fixtures/ca.csr +12 -0
  14. data/fixtures/ca.key +15 -0
  15. data/fixtures/client.crt +15 -0
  16. data/fixtures/client.csr +12 -0
  17. data/fixtures/client.key +15 -0
  18. data/lib/certmeister.rb +14 -0
  19. data/lib/certmeister/base.rb +92 -0
  20. data/lib/certmeister/config.rb +129 -0
  21. data/lib/certmeister/in_memory_store.rb +43 -0
  22. data/lib/certmeister/policy.rb +21 -0
  23. data/lib/certmeister/policy/blackhole.rb +15 -0
  24. data/lib/certmeister/policy/chain_all.rb +36 -0
  25. data/lib/certmeister/policy/domain.rb +37 -0
  26. data/lib/certmeister/policy/existing.rb +32 -0
  27. data/lib/certmeister/policy/fcrdns.rb +40 -0
  28. data/lib/certmeister/policy/noop.rb +15 -0
  29. data/lib/certmeister/policy/psk.rb +37 -0
  30. data/lib/certmeister/policy/response.rb +24 -0
  31. data/lib/certmeister/response.rb +47 -0
  32. data/lib/certmeister/store_error.rb +6 -0
  33. data/lib/certmeister/test/memory_store_interface.rb +54 -0
  34. data/lib/certmeister/version.rb +5 -0
  35. data/signit.rb +39 -0
  36. data/spec/certmeister/base_spec.rb +205 -0
  37. data/spec/certmeister/config_spec.rb +170 -0
  38. data/spec/certmeister/in_memory_store_spec.rb +40 -0
  39. data/spec/certmeister/policy/blackhole_spec.rb +19 -0
  40. data/spec/certmeister/policy/chain_all_spec.rb +40 -0
  41. data/spec/certmeister/policy/domain_spec.rb +38 -0
  42. data/spec/certmeister/policy/existing_spec.rb +39 -0
  43. data/spec/certmeister/policy/fcrdns_spec.rb +45 -0
  44. data/spec/certmeister/policy/noop_spec.rb +17 -0
  45. data/spec/certmeister/policy/psk_spec.rb +38 -0
  46. data/spec/certmeister/policy/response_spec.rb +35 -0
  47. data/spec/certmeister/response_spec.rb +73 -0
  48. data/spec/helpers/certmeister_config_helper.rb +21 -0
  49. data/spec/helpers/certmeister_fetching_request_helper.rb +9 -0
  50. data/spec/helpers/certmeister_policy_helper.rb +14 -0
  51. data/spec/helpers/certmeister_removing_request_helper.rb +9 -0
  52. data/spec/helpers/certmeister_signing_request_helper.rb +10 -0
  53. data/spec/spec_helper.rb +20 -0
  54. metadata +159 -0
@@ -0,0 +1,38 @@
1
+ require 'spec_helper'
2
+
3
+ require 'certmeister/policy/domain'
4
+
5
+ describe Certmeister::Policy::Domain do
6
+
7
+ subject { Certmeister::Policy::Domain.new(['hetzner.africa']) }
8
+
9
+ it "must be configured with a list of domains" do
10
+ expected_error = "enumerable collection of domains required"
11
+ expect { Certmeister::Policy::Domain.new }.to raise_error(ArgumentError)
12
+ expect { Certmeister::Policy::Domain.new('example.com') }.to raise_error(ArgumentError, expected_error)
13
+ expect { Certmeister::Policy::Domain.new([]) }.to raise_error(ArgumentError, expected_error)
14
+ end
15
+
16
+ it "demands a request" do
17
+ expect { subject.authenticate }.to raise_error(ArgumentError)
18
+ end
19
+
20
+ it "refuses to authenticate a request with a missing cn" do
21
+ response = subject.authenticate({anything: 'something'})
22
+ expect(response).to_not be_authenticated
23
+ expect(response.error).to eql "missing cn"
24
+ end
25
+
26
+ it "refuses to authenticate a request with a cn in an unknown domain" do
27
+ response = subject.authenticate({anything: 'something', cn: 'axl.starjuice.net'})
28
+ expect(response).to_not be_authenticated
29
+ expect(response.error).to eql "cn in unknown domain"
30
+ end
31
+
32
+ it "authenticates any request with a cn in a known domain" do
33
+ response = subject.authenticate({anything: 'something', cn: 'axl.hetzner.africa'})
34
+ expect(response).to be_authenticated
35
+ end
36
+
37
+
38
+ end
@@ -0,0 +1,39 @@
1
+ require 'spec_helper'
2
+
3
+ require 'certmeister/policy/existing'
4
+ require 'certmeister/in_memory_store'
5
+
6
+ describe Certmeister::Policy::Existing do
7
+
8
+ subject { Certmeister::Policy::Existing.new(Certmeister::InMemoryStore.new) }
9
+
10
+ it "must be configured with access to the store" do
11
+ expect { subject.class.new }.to raise_error(ArgumentError)
12
+ expect { subject.class.new(Object.new) }.to raise_error(ArgumentError)
13
+ expect { subject }.to_not raise_error
14
+ end
15
+
16
+ it "demands a request" do
17
+ expect { subject.authenticate }.to raise_error(ArgumentError)
18
+ end
19
+
20
+ context "when the store contains a cert for axl.hetzner.africa" do
21
+
22
+ subject { Certmeister::Policy::Existing.new(Certmeister::InMemoryStore.new({"axl.hetzner.africa" => "...cert..."})) }
23
+
24
+ it "refuses to authenticate a request for axl.hetzner.africa" do
25
+ response = subject.authenticate(cn: 'axl.hetzner.africa')
26
+ expect(response).to_not be_authenticated
27
+ expect(response.error).to match /exists/
28
+ end
29
+
30
+ it "authenticates requests for other common names" do
31
+ response = subject.authenticate(cn: 'bob.example.com')
32
+ expect(response).to be_authenticated
33
+ end
34
+
35
+ end
36
+
37
+
38
+
39
+ end
@@ -0,0 +1,45 @@
1
+ require 'spec_helper'
2
+
3
+ require 'certmeister/policy/fcrdns'
4
+
5
+ describe Certmeister::Policy::Fcrdns do
6
+
7
+ it "demands a request" do
8
+ expect { subject.authenticate }.to raise_error(ArgumentError)
9
+ end
10
+
11
+ it "refuses to authenticate a request with a missing cn" do
12
+ response = subject.authenticate({ip: '127.0.0.1'})
13
+ expect(response).to_not be_authenticated
14
+ expect(response.error).to eql "missing cn"
15
+ end
16
+
17
+ it "refuses to authenticate a request with a missing ip" do
18
+ response = subject.authenticate({cn: 'localhost'})
19
+ expect(response).to_not be_authenticated
20
+ expect(response.error).to eql "missing ip"
21
+ end
22
+
23
+ it "refuses to authenticate a request with an ip that does not have fcrdns that matches the cn" do
24
+ response = subject.authenticate({cn: 'bad.example.com', ip: '127.0.0.1'})
25
+ expect(response).to_not be_authenticated
26
+ expect(response.error).to eql "cn in unknown domain"
27
+ end
28
+
29
+ it "authenticates any request with an ip that does not have fcrdns that matches the cn" do
30
+ response = subject.authenticate({cn: 'localhost', ip: '127.0.0.1'})
31
+ expect(response).to be_authenticated
32
+ end
33
+
34
+ describe "error handling" do
35
+
36
+ it "refuses to authenticate a request when a DNS failure occurs" do
37
+ Resolv::DNS.any_instance.stub(:getnames).with('nonsense').and_raise(Resolv::ResolvError.new("cannot interpret as address: nonsense"))
38
+ response = subject.authenticate({cn: 'localhost', ip: 'nonsense'})
39
+ expect(response).to_not be_authenticated
40
+ expect(response.error).to eql "DNS error (cannot interpret as address: nonsense)"
41
+ end
42
+
43
+ end
44
+
45
+ end
@@ -0,0 +1,17 @@
1
+ require 'spec_helper'
2
+
3
+ require 'certmeister/policy/noop'
4
+
5
+ describe Certmeister::Policy::Noop do
6
+
7
+ it "demands a request" do
8
+ expect { subject.authenticate }.to raise_error(ArgumentError)
9
+ end
10
+
11
+ it "authenticates any non-empty request" do
12
+ response = subject.authenticate(anything: 'something')
13
+ expect(response).to be_authenticated
14
+ expect(response.error).to be_nil
15
+ end
16
+
17
+ end
@@ -0,0 +1,38 @@
1
+ require 'spec_helper'
2
+
3
+ require 'certmeister/policy/psk'
4
+
5
+ describe Certmeister::Policy::Psk do
6
+
7
+ subject { Certmeister::Policy::Psk.new(['secret']) }
8
+
9
+ it "must be configured with a list of psks" do
10
+ expected_error = "enumerable collection of psks required"
11
+ expect { Certmeister::Policy::Psk.new }.to raise_error(ArgumentError)
12
+ expect { Certmeister::Policy::Psk.new('secret') }.to raise_error(ArgumentError, expected_error)
13
+ expect { Certmeister::Policy::Psk.new([]) }.to raise_error(ArgumentError, expected_error)
14
+ end
15
+
16
+ it "demands a request" do
17
+ expect { subject.authenticate }.to raise_error(ArgumentError)
18
+ end
19
+
20
+ it "refuses to authenticate a request with a missing psk" do
21
+ response = subject.authenticate({anything: 'something'})
22
+ expect(response).to_not be_authenticated
23
+ expect(response.error).to eql "missing psk"
24
+ end
25
+
26
+ it "refuses to authenticate a request with an unknown psk" do
27
+ response = subject.authenticate({anything: 'something', psk: 'wrong'})
28
+ expect(response).to_not be_authenticated
29
+ expect(response.error).to eql "unknown psk"
30
+ end
31
+
32
+ it "authenticates any request with a known psk" do
33
+ response = subject.authenticate({anything: 'something', psk: 'secret'})
34
+ expect(response).to be_authenticated
35
+ end
36
+
37
+
38
+ end
@@ -0,0 +1,35 @@
1
+ require 'spec_helper'
2
+
3
+ require 'certmeister/policy/response'
4
+
5
+ describe Certmeister::Policy::Response do
6
+
7
+ describe "on error" do
8
+
9
+ it "is not authenticated" do
10
+ response = Certmeister::Policy::Response.new(nil, "you smell wrong")
11
+ expect(response).to_not be_authenticated
12
+ end
13
+
14
+ it "provides the error" do
15
+ response = Certmeister::Policy::Response.new(nil, "you smell wrong")
16
+ expect(response.error).to eql "you smell wrong"
17
+ end
18
+
19
+ end
20
+
21
+ describe "on success" do
22
+
23
+ it "is authenticated" do
24
+ response = Certmeister::Policy::Response.new(true, nil)
25
+ expect(response).to be_authenticated
26
+ end
27
+
28
+ it "provides no error" do
29
+ response = Certmeister::Policy::Response.new(true, nil)
30
+ expect(response.error).to be_nil
31
+ end
32
+
33
+ end
34
+
35
+ end
@@ -0,0 +1,73 @@
1
+ require 'spec_helper'
2
+
3
+ require 'certmeister/response'
4
+
5
+ describe Certmeister::Response do
6
+
7
+ let(:pem) { File.read('fixtures/client.crt') }
8
+
9
+ it "cannot be created with pem and error" do
10
+ expect { Certmeister::Response.new(pem, "silly") }.to raise_error(ArgumentError)
11
+ end
12
+
13
+ describe "on error" do
14
+
15
+ subject { Certmeister::Response.new(nil, "something went wrong") }
16
+
17
+ it "provides the error" do
18
+ expect(subject.error).to eql "something went wrong"
19
+ end
20
+
21
+ it "doesn't provide the PEM-encoded X509 certificate as a string" do
22
+ expect(subject.pem).to be_nil
23
+ end
24
+
25
+ it "offers appropriate boolean flags" do
26
+ expect(subject.hit?).to be_false
27
+ expect(subject.miss?).to be_false
28
+ expect(subject.error?).to be_true
29
+ end
30
+
31
+ end
32
+
33
+ describe "on miss (i.e. not found)" do
34
+
35
+ subject { Certmeister::Response.new(nil, nil) }
36
+
37
+ it "has no error" do
38
+ expect(subject.error).to be_nil
39
+ end
40
+
41
+ it "doesn't provide the PEM-encoded X509 certificate as a string" do
42
+ expect(subject.pem).to be_nil
43
+ end
44
+
45
+ it "offers appropriate boolean flags" do
46
+ expect(subject.hit?).to be_false
47
+ expect(subject.miss?).to be_true
48
+ expect(subject.error?).to be_false
49
+ end
50
+
51
+ end
52
+
53
+ describe "on hit (success)" do
54
+
55
+ subject { Certmeister::Response.new(pem, nil) }
56
+
57
+ it "has no error" do
58
+ expect(subject.error).to be_nil
59
+ end
60
+
61
+ it "provides the PEM-encoded X509 certificate as a string" do
62
+ expect(subject.pem).to eql pem
63
+ end
64
+
65
+ it "offers appropriate boolean flags" do
66
+ expect(subject.hit?).to be_true
67
+ expect(subject.miss?).to be_false
68
+ expect(subject.error?).to be_false
69
+ end
70
+
71
+ end
72
+
73
+ end
@@ -0,0 +1,21 @@
1
+ require 'certmeister/in_memory_store'
2
+ require 'certmeister/policy/noop'
3
+
4
+ module CertmeisterConfigHelper
5
+
6
+ def self.valid_config_options
7
+ ca_cert = File.read('fixtures/ca.crt')
8
+ ca_key = File.read('fixtures/ca.key')
9
+ { ca_cert: ca_cert,
10
+ ca_key: ca_key,
11
+ store: Certmeister::InMemoryStore.new,
12
+ sign_policy: Certmeister::Policy::Noop.new,
13
+ fetch_policy: Certmeister::Policy::Noop.new,
14
+ remove_policy: Certmeister::Policy::Noop.new }
15
+ end
16
+
17
+ def self.valid_config
18
+ Certmeister::Config.new(valid_config_options)
19
+ end
20
+
21
+ end
@@ -0,0 +1,9 @@
1
+ module CertmeisterFetchingRequestHelper
2
+
3
+ def self.valid_request
4
+ { cn: 'axl.hetzner.africa',
5
+ ip: '127.0.0.1',
6
+ psk: 'secret' }
7
+ end
8
+
9
+ end
@@ -0,0 +1,14 @@
1
+ # This policy is broken because it violates the rule that an empty request must always be refused.
2
+
3
+ module CertmeisterPolicyHelper
4
+
5
+ class BrokenPolicy
6
+
7
+ def authenticate(request)
8
+ :white_elephant
9
+ end
10
+
11
+ end
12
+
13
+ end
14
+
@@ -0,0 +1,9 @@
1
+ module CertmeisterRemovingRequestHelper
2
+
3
+ def self.valid_request
4
+ { cn: 'axl.hetzner.africa',
5
+ ip: '127.0.0.1',
6
+ psk: 'secret' }
7
+ end
8
+
9
+ end
@@ -0,0 +1,10 @@
1
+ module CertmeisterSigningRequestHelper
2
+
3
+ def self.valid_request
4
+ { cn: 'axl.hetzner.africa',
5
+ ip: '127.0.0.1',
6
+ csr: File.read('fixtures/client.csr'),
7
+ psk: 'secret' }
8
+ end
9
+
10
+ end
@@ -0,0 +1,20 @@
1
+ # This file was generated by the `rspec --init` command. Conventionally, all
2
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
3
+ # Require this file using `require "spec_helper"` to ensure that it is only
4
+ # loaded once.
5
+ #
6
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
7
+ RSpec.configure do |config|
8
+ config.treat_symbols_as_metadata_keys_with_true_values = true
9
+ config.run_all_when_everything_filtered = true
10
+ config.filter_run :focus
11
+
12
+ # Run specs in random order to surface order dependencies. If you find an
13
+ # order dependency and want to debug it, you can fix the order by providing
14
+ # the seed, which is printed after each run.
15
+ # --seed 1234
16
+ config.order = 'random'
17
+ end
18
+
19
+ $LOAD_PATH.unshift File.dirname(__FILE__)
20
+ $LOAD_PATH.unshift File.join(File.dirname(__FILE__), '..', 'lib')
metadata ADDED
@@ -0,0 +1,159 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: certmeister
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Sheldon Hearn
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-01-29 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.5'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.5'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: '2.14'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: '2.14'
55
+ description: Certificate authority that can be configured to make decisions about
56
+ whether to autosign certificate signing requests for clients. This gem provides
57
+ the protocol-agnostic library, which is expected to be used within something like
58
+ an HTTP REST service.
59
+ email:
60
+ - sheldonh@starjuice.net
61
+ executables: []
62
+ extensions: []
63
+ extra_rdoc_files: []
64
+ files:
65
+ - .gitignore
66
+ - .rspec
67
+ - .ruby-gemset
68
+ - .ruby-version
69
+ - Gemfile
70
+ - Gemfile.lock
71
+ - LICENSE
72
+ - README.md
73
+ - Rakefile
74
+ - certmeister.gemspec
75
+ - fixtures/ca.crt
76
+ - fixtures/ca.csr
77
+ - fixtures/ca.key
78
+ - fixtures/client.crt
79
+ - fixtures/client.csr
80
+ - fixtures/client.key
81
+ - lib/certmeister.rb
82
+ - lib/certmeister/base.rb
83
+ - lib/certmeister/config.rb
84
+ - lib/certmeister/in_memory_store.rb
85
+ - lib/certmeister/policy.rb
86
+ - lib/certmeister/policy/blackhole.rb
87
+ - lib/certmeister/policy/chain_all.rb
88
+ - lib/certmeister/policy/domain.rb
89
+ - lib/certmeister/policy/existing.rb
90
+ - lib/certmeister/policy/fcrdns.rb
91
+ - lib/certmeister/policy/noop.rb
92
+ - lib/certmeister/policy/psk.rb
93
+ - lib/certmeister/policy/response.rb
94
+ - lib/certmeister/response.rb
95
+ - lib/certmeister/store_error.rb
96
+ - lib/certmeister/test/memory_store_interface.rb
97
+ - lib/certmeister/version.rb
98
+ - signit.rb
99
+ - spec/certmeister/base_spec.rb
100
+ - spec/certmeister/config_spec.rb
101
+ - spec/certmeister/in_memory_store_spec.rb
102
+ - spec/certmeister/policy/blackhole_spec.rb
103
+ - spec/certmeister/policy/chain_all_spec.rb
104
+ - spec/certmeister/policy/domain_spec.rb
105
+ - spec/certmeister/policy/existing_spec.rb
106
+ - spec/certmeister/policy/fcrdns_spec.rb
107
+ - spec/certmeister/policy/noop_spec.rb
108
+ - spec/certmeister/policy/psk_spec.rb
109
+ - spec/certmeister/policy/response_spec.rb
110
+ - spec/certmeister/response_spec.rb
111
+ - spec/helpers/certmeister_config_helper.rb
112
+ - spec/helpers/certmeister_fetching_request_helper.rb
113
+ - spec/helpers/certmeister_policy_helper.rb
114
+ - spec/helpers/certmeister_removing_request_helper.rb
115
+ - spec/helpers/certmeister_signing_request_helper.rb
116
+ - spec/spec_helper.rb
117
+ homepage: https://github.com/sheldonh/certmeister
118
+ licenses:
119
+ - MIT
120
+ metadata: {}
121
+ post_install_message:
122
+ rdoc_options: []
123
+ require_paths:
124
+ - lib
125
+ required_ruby_version: !ruby/object:Gem::Requirement
126
+ requirements:
127
+ - - '>='
128
+ - !ruby/object:Gem::Version
129
+ version: '0'
130
+ required_rubygems_version: !ruby/object:Gem::Requirement
131
+ requirements:
132
+ - - '>='
133
+ - !ruby/object:Gem::Version
134
+ version: '0'
135
+ requirements: []
136
+ rubyforge_project:
137
+ rubygems_version: 2.2.1
138
+ signing_key:
139
+ specification_version: 4
140
+ summary: Conditionally autosigning certificate authority.
141
+ test_files:
142
+ - spec/certmeister/base_spec.rb
143
+ - spec/certmeister/config_spec.rb
144
+ - spec/certmeister/in_memory_store_spec.rb
145
+ - spec/certmeister/policy/blackhole_spec.rb
146
+ - spec/certmeister/policy/chain_all_spec.rb
147
+ - spec/certmeister/policy/domain_spec.rb
148
+ - spec/certmeister/policy/existing_spec.rb
149
+ - spec/certmeister/policy/fcrdns_spec.rb
150
+ - spec/certmeister/policy/noop_spec.rb
151
+ - spec/certmeister/policy/psk_spec.rb
152
+ - spec/certmeister/policy/response_spec.rb
153
+ - spec/certmeister/response_spec.rb
154
+ - spec/helpers/certmeister_config_helper.rb
155
+ - spec/helpers/certmeister_fetching_request_helper.rb
156
+ - spec/helpers/certmeister_policy_helper.rb
157
+ - spec/helpers/certmeister_removing_request_helper.rb
158
+ - spec/helpers/certmeister_signing_request_helper.rb
159
+ - spec/spec_helper.rb