bullion 0.1.2 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.roxanne.yml +14 -0
- data/.rubocop.yml +25 -6
- data/.ruby-version +1 -0
- data/.travis.yml +2 -1
- data/Dockerfile +2 -2
- data/Gemfile +1 -1
- data/Gemfile.lock +99 -89
- data/README.md +2 -2
- data/Rakefile +40 -37
- data/bin/console +3 -3
- data/bullion.gemspec +38 -36
- data/config/puma.rb +1 -1
- data/config.ru +5 -5
- data/db/migrate/20210104060422_create_certificates.rb +1 -1
- data/db/migrate/20210105060406_create_orders.rb +1 -1
- data/db/migrate/20210106052306_create_authorizations.rb +1 -1
- data/db/schema.rb +20 -21
- data/lib/bullion/acme/error.rb +9 -9
- data/lib/bullion/challenge_client.rb +4 -4
- data/lib/bullion/challenge_clients/dns.rb +36 -21
- data/lib/bullion/challenge_clients/http.rb +12 -8
- data/lib/bullion/helpers/acme.rb +30 -40
- data/lib/bullion/helpers/service.rb +2 -2
- data/lib/bullion/helpers/ssl.rb +50 -42
- data/lib/bullion/models/account.rb +1 -1
- data/lib/bullion/models/certificate.rb +2 -2
- data/lib/bullion/models/challenge.rb +5 -5
- data/lib/bullion/models/nonce.rb +1 -1
- data/lib/bullion/models.rb +6 -6
- data/lib/bullion/rspec/challenge_clients/dns.rb +22 -0
- data/lib/bullion/rspec/challenge_clients/http.rb +16 -0
- data/lib/bullion/service.rb +3 -2
- data/lib/bullion/services/ca.rb +107 -91
- data/lib/bullion/services/ping.rb +6 -6
- data/lib/bullion/version.rb +3 -3
- data/lib/bullion.rb +58 -45
- data/scripts/build.sh +3 -0
- data/scripts/release.sh +9 -0
- data/scripts/test.sh +6 -0
- metadata +65 -30
data/lib/bullion/services/ca.rb
CHANGED
@@ -15,174 +15,174 @@ module Bullion
|
|
15
15
|
|
16
16
|
after do
|
17
17
|
if request.options?
|
18
|
-
@allowed_types ||= [
|
19
|
-
headers
|
18
|
+
@allowed_types ||= ["POST"]
|
19
|
+
headers "Access-Control-Allow-Methods" => @allowed_types
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
23
|
-
options
|
24
|
-
@allowed_types = [
|
23
|
+
options "/directory" do
|
24
|
+
@allowed_types = ["GET"]
|
25
25
|
halt 200
|
26
26
|
end
|
27
27
|
|
28
|
-
options
|
28
|
+
options "/nonces" do
|
29
29
|
@allowed_types = %w[HEAD GET]
|
30
30
|
halt 200
|
31
31
|
end
|
32
32
|
|
33
|
-
options
|
33
|
+
options "/accounts" do
|
34
34
|
halt 200
|
35
35
|
end
|
36
36
|
|
37
|
-
options
|
37
|
+
options "/accounts/:id" do
|
38
38
|
halt 200
|
39
39
|
end
|
40
40
|
|
41
|
-
options
|
41
|
+
options "/accounts/:id/orders" do
|
42
42
|
halt 200
|
43
43
|
end
|
44
44
|
|
45
|
-
options
|
45
|
+
options "/orders" do
|
46
46
|
halt 200
|
47
47
|
end
|
48
48
|
|
49
|
-
options
|
49
|
+
options "/orders/:id" do
|
50
50
|
halt 200
|
51
51
|
end
|
52
52
|
|
53
|
-
options
|
53
|
+
options "/orders/:id/finalize" do
|
54
54
|
halt 200
|
55
55
|
end
|
56
56
|
|
57
|
-
options
|
57
|
+
options "/authorizations/:id" do
|
58
58
|
halt 200
|
59
59
|
end
|
60
60
|
|
61
|
-
options
|
61
|
+
options "/challenges/:id" do
|
62
62
|
halt 200
|
63
63
|
end
|
64
64
|
|
65
|
-
options
|
65
|
+
options "/certificates/:id" do
|
66
66
|
halt 200
|
67
67
|
end
|
68
68
|
|
69
69
|
# Non-standard endpoint that returns the CA bundle for Bullion
|
70
70
|
# Trusting this bundle should be sufficient to trust all Bullion-issued certs
|
71
|
-
options
|
72
|
-
@allowed_types = [
|
71
|
+
options "/cabundle" do
|
72
|
+
@allowed_types = ["GET"]
|
73
73
|
halt 200
|
74
74
|
end
|
75
75
|
|
76
76
|
# The directory is used to find all required URLs for the ACME endpoints
|
77
77
|
# @see https://tools.ietf.org/html/rfc8555#section-7.1.1
|
78
|
-
get
|
79
|
-
content_type
|
78
|
+
get "/directory" do
|
79
|
+
content_type "application/json"
|
80
80
|
|
81
81
|
{
|
82
|
-
newNonce: uri(
|
83
|
-
newAccount: uri(
|
84
|
-
newOrder: uri(
|
85
|
-
revokeCert: uri(
|
86
|
-
keyChange: uri(
|
82
|
+
newNonce: uri("/nonces"),
|
83
|
+
newAccount: uri("/accounts"),
|
84
|
+
newOrder: uri("/orders"),
|
85
|
+
revokeCert: uri("/revokecert"),
|
86
|
+
keyChange: uri("/keychanges"),
|
87
87
|
# non-standard entries:
|
88
|
-
caBundle: uri(
|
88
|
+
caBundle: uri("/cabundle")
|
89
89
|
}.to_json
|
90
90
|
end
|
91
91
|
|
92
92
|
# Responds with Bullion's PEM-encoded public cert
|
93
|
-
get
|
93
|
+
get "/cabundle" do
|
94
94
|
expires 3600 * 48, :public, :must_revalidate
|
95
|
-
content_type
|
95
|
+
content_type "application/x-pem-file"
|
96
96
|
|
97
|
-
attachment
|
97
|
+
attachment "cabundle.pem"
|
98
98
|
Bullion.ca_cert.to_pem
|
99
99
|
end
|
100
100
|
|
101
101
|
# Retrieves a Nonce via a HEAD request
|
102
102
|
# @see https://tools.ietf.org/html/rfc8555#section-7.2
|
103
|
-
head
|
104
|
-
add_acme_headers @new_nonce, additional: {
|
103
|
+
head "/nonces" do
|
104
|
+
add_acme_headers @new_nonce, additional: { "Cache-Control" => "no-store" }
|
105
105
|
|
106
106
|
halt 200
|
107
107
|
end
|
108
108
|
|
109
109
|
# Retrieves a Nonce via a GET request
|
110
110
|
# @see https://tools.ietf.org/html/rfc8555#section-7.2
|
111
|
-
get
|
112
|
-
add_acme_headers @new_nonce, additional: {
|
111
|
+
get "/nonces" do
|
112
|
+
add_acme_headers @new_nonce, additional: { "Cache-Control" => "no-store" }
|
113
113
|
|
114
114
|
halt 204
|
115
115
|
end
|
116
116
|
|
117
117
|
# Creates an account or verifies that an account exists
|
118
118
|
# @see https://tools.ietf.org/html/rfc8555#section-7.3
|
119
|
-
post
|
119
|
+
post "/accounts" do
|
120
120
|
header_data = JSON.parse(Base64.decode64(@json_body[:protected]))
|
121
121
|
begin
|
122
|
-
parse_acme_jwt(header_data[
|
122
|
+
parse_acme_jwt(header_data["jwk"], validate_nonce: false)
|
123
123
|
|
124
124
|
validate_account_data(@payload_data)
|
125
125
|
rescue Bullion::Acme::Error => e
|
126
|
-
content_type
|
126
|
+
content_type "application/problem+json"
|
127
127
|
halt 400, { type: e.acme_error, detail: e.message }.to_json
|
128
128
|
end
|
129
129
|
|
130
130
|
user = Models::Account.where(
|
131
|
-
public_key: header_data[
|
131
|
+
public_key: header_data["jwk"]
|
132
132
|
).first
|
133
133
|
|
134
|
-
if @payload_data[
|
135
|
-
content_type
|
136
|
-
halt 400, { type:
|
134
|
+
if @payload_data["onlyReturnExisting"]
|
135
|
+
content_type "application/problem+json"
|
136
|
+
halt 400, { type: "urn:ietf:params:acme:error:accountDoesNotExist" }.to_json unless user
|
137
137
|
end
|
138
138
|
|
139
|
-
user ||= Models::Account.new(public_key: header_data[
|
139
|
+
user ||= Models::Account.new(public_key: header_data["jwk"])
|
140
140
|
user.tos_agreed = true
|
141
|
-
user.contacts = @payload_data[
|
141
|
+
user.contacts = @payload_data["contact"]
|
142
142
|
user.save
|
143
143
|
|
144
|
-
content_type
|
145
|
-
add_acme_headers @new_nonce, additional: {
|
144
|
+
content_type "application/json"
|
145
|
+
add_acme_headers @new_nonce, additional: { "Location" => uri("/accounts/#{user.id}") }
|
146
146
|
|
147
147
|
halt 201, {
|
148
|
-
|
149
|
-
|
150
|
-
|
148
|
+
status: user.tos_agreed? ? "valid" : "pending",
|
149
|
+
contact: user.contacts,
|
150
|
+
orders: uri("/accounts/#{user.id}/orders")
|
151
151
|
}.to_json
|
152
152
|
end
|
153
153
|
|
154
154
|
# Endpoint for updating accounts
|
155
155
|
# @see https://tools.ietf.org/html/rfc8555#section-7.3.2
|
156
|
-
post
|
156
|
+
post "/accounts/:id" do
|
157
157
|
parse_acme_jwt
|
158
158
|
|
159
159
|
unless params[:id] == @user.id
|
160
|
-
content_type
|
160
|
+
content_type "application/json"
|
161
161
|
add_acme_headers @new_nonce
|
162
162
|
|
163
|
-
halt 403, { error:
|
163
|
+
halt 403, { error: "Accounts can only view or update themselves" }.to_json
|
164
164
|
end
|
165
165
|
|
166
|
-
content_type
|
166
|
+
content_type "application/json"
|
167
167
|
|
168
168
|
{
|
169
|
-
|
170
|
-
|
171
|
-
|
169
|
+
status: "valid",
|
170
|
+
orders: uri("/accounts/#{@user.id}/orders"),
|
171
|
+
contact: @user.contacts
|
172
172
|
}.to_json
|
173
173
|
end
|
174
174
|
|
175
|
-
post
|
175
|
+
post "/accounts/:id/orders" do
|
176
176
|
parse_acme_jwt
|
177
177
|
|
178
178
|
unless params[:id] == @user.id
|
179
|
-
content_type
|
179
|
+
content_type "application/json"
|
180
180
|
add_acme_headers @new_nonce
|
181
181
|
|
182
|
-
halt 403, { error:
|
182
|
+
halt 403, { error: "Accounts can only view or update themselves" }.to_json
|
183
183
|
end
|
184
184
|
|
185
|
-
content_type
|
185
|
+
content_type "application/json"
|
186
186
|
add_acme_headers @new_nonce
|
187
187
|
|
188
188
|
{
|
@@ -192,22 +192,22 @@ module Bullion
|
|
192
192
|
|
193
193
|
# Endpoint for creating new orders
|
194
194
|
# @see https://tools.ietf.org/html/rfc8555#section-7.4
|
195
|
-
post
|
195
|
+
post "/orders" do
|
196
196
|
parse_acme_jwt
|
197
197
|
|
198
198
|
# Only identifiers of type "dns" are supported
|
199
|
-
identifiers = @payload_data[
|
199
|
+
identifiers = @payload_data["identifiers"].select { |i| i["type"] == "dns" }
|
200
200
|
|
201
201
|
validate_order(@payload_data)
|
202
202
|
|
203
203
|
order = @user.start_order(
|
204
|
-
identifiers
|
205
|
-
not_before: @payload_data[
|
206
|
-
not_after: @payload_data[
|
204
|
+
identifiers:,
|
205
|
+
not_before: @payload_data["notBefore"],
|
206
|
+
not_after: @payload_data["notAfter"]
|
207
207
|
)
|
208
208
|
|
209
|
-
content_type
|
210
|
-
add_acme_headers @new_nonce, additional: {
|
209
|
+
content_type "application/json"
|
210
|
+
add_acme_headers @new_nonce, additional: { "Location" => uri("/orders/#{order.id}") }
|
211
211
|
|
212
212
|
halt 201, {
|
213
213
|
status: order.status,
|
@@ -219,15 +219,15 @@ module Bullion
|
|
219
219
|
finalize: uri("/orders/#{order.id}/finalize")
|
220
220
|
}.to_json
|
221
221
|
rescue Bullion::Acme::Error => e
|
222
|
-
content_type
|
222
|
+
content_type "application/problem+json"
|
223
223
|
halt 400, { type: e.acme_error, detail: e.message }.to_json
|
224
224
|
end
|
225
225
|
|
226
226
|
# Retrieve existing Orders
|
227
|
-
post
|
227
|
+
post "/orders/:id" do
|
228
228
|
parse_acme_jwt
|
229
229
|
|
230
|
-
content_type
|
230
|
+
content_type "application/json"
|
231
231
|
add_acme_headers @new_nonce
|
232
232
|
|
233
233
|
order = Models::Order.find(params[:id])
|
@@ -242,40 +242,43 @@ module Bullion
|
|
242
242
|
finalize: uri("/orders/#{order.id}/finalize")
|
243
243
|
}
|
244
244
|
|
245
|
-
data[:certificate] = uri("/certificates/#{order.certificate.id}") if order.status ==
|
245
|
+
data[:certificate] = uri("/certificates/#{order.certificate.id}") if order.status == "valid"
|
246
246
|
|
247
247
|
data.to_json
|
248
|
+
rescue Bullion::Acme::Error => e
|
249
|
+
content_type "application/problem+json"
|
250
|
+
halt 400, { type: e.acme_error, detail: e.message }.to_json
|
248
251
|
end
|
249
252
|
|
250
253
|
# Submit an order for finalization/signing
|
251
254
|
# @see https://tools.ietf.org/html/rfc8555#section-7.4
|
252
|
-
post
|
255
|
+
post "/orders/:id/finalize" do
|
253
256
|
parse_acme_jwt
|
254
257
|
|
255
|
-
|
256
|
-
add_acme_headers @new_nonce, additional: { 'Location' => uri("/orders/#{order.id}") }
|
258
|
+
order = Models::Order.find(params[:id])
|
257
259
|
|
258
|
-
|
260
|
+
content_type "application/json"
|
261
|
+
add_acme_headers @new_nonce, additional: { "Location" => uri("/orders/#{order.id}") }
|
262
|
+
|
263
|
+
raw_csr_data = Base64.urlsafe_decode64(@payload_data["csr"])
|
259
264
|
encoded_csr = Base64.encode64(raw_csr_data)
|
260
265
|
|
261
266
|
csr_data = openssl_compat_csr(encoded_csr)
|
262
267
|
|
263
268
|
csr = OpenSSL::X509::Request.new(csr_data)
|
264
269
|
|
265
|
-
order
|
266
|
-
|
267
|
-
unless validate_csr(csr) && validate_acme_csr(order, csr)
|
268
|
-
content_type 'application/problem+json'
|
270
|
+
unless validate_acme_csr(order, csr)
|
271
|
+
content_type "application/problem+json"
|
269
272
|
halt 400, {
|
270
|
-
type: Bullion::Acme::Errors::
|
271
|
-
detail:
|
273
|
+
type: Bullion::Acme::Errors::BadCsr.new.acme_error,
|
274
|
+
detail: "CSR failed validation"
|
272
275
|
}.to_json
|
273
276
|
end
|
274
277
|
|
275
|
-
cert_id = sign_csr(csr, @user.contacts.first
|
278
|
+
cert_id = sign_csr(csr, @user.contacts.first).last
|
276
279
|
|
277
280
|
order.certificate_id = cert_id
|
278
|
-
order.status =
|
281
|
+
order.status = "valid"
|
279
282
|
order.save
|
280
283
|
|
281
284
|
data = {
|
@@ -288,17 +291,20 @@ module Bullion
|
|
288
291
|
finalize: uri("/orders/#{order.id}/finalize")
|
289
292
|
}
|
290
293
|
|
291
|
-
data[:certificate] = uri("/certificates/#{order.certificate.id}") if order.status ==
|
294
|
+
data[:certificate] = uri("/certificates/#{order.certificate.id}") if order.status == "valid"
|
292
295
|
|
293
296
|
data.to_json
|
297
|
+
rescue Bullion::Acme::Error => e
|
298
|
+
content_type "application/problem+json"
|
299
|
+
halt 422, { type: e.acme_error, detail: e.message }.to_json
|
294
300
|
end
|
295
301
|
|
296
302
|
# Shows that the client controls the account private key
|
297
303
|
# @see https://tools.ietf.org/html/rfc8555#section-7.5
|
298
|
-
post
|
304
|
+
post "/authorizations/:id" do
|
299
305
|
parse_acme_jwt
|
300
306
|
|
301
|
-
content_type
|
307
|
+
content_type "application/json"
|
302
308
|
add_acme_headers @new_nonce
|
303
309
|
|
304
310
|
authorization = Models::Authorization.find(params[:id])
|
@@ -314,27 +320,30 @@ module Bullion
|
|
314
320
|
chash[:url] = uri("/challenges/#{c.id}")
|
315
321
|
chash[:token] = c.token
|
316
322
|
chash[:status] = c.status
|
317
|
-
chash[:validated] = c.validated if c.status ==
|
323
|
+
chash[:validated] = c.validated if c.status == "valid"
|
318
324
|
|
319
325
|
chash
|
320
326
|
end
|
321
327
|
}
|
322
328
|
|
323
329
|
data.to_json
|
330
|
+
rescue Bullion::Acme::Error => e
|
331
|
+
content_type "application/problem+json"
|
332
|
+
halt 422, { type: e.acme_error, detail: e.message }.to_json
|
324
333
|
end
|
325
334
|
|
326
335
|
# Starts server verification of a challenge (either HTTP call or DNS lookup)
|
327
336
|
# @see https://tools.ietf.org/html/rfc8555#section-7.5.1
|
328
|
-
post
|
337
|
+
post "/challenges/:id" do
|
329
338
|
parse_acme_jwt
|
330
339
|
|
331
|
-
content_type
|
340
|
+
content_type "application/json"
|
332
341
|
add_acme_headers @new_nonce
|
333
342
|
|
334
343
|
challenge = Models::Challenge.find(params[:id])
|
335
344
|
|
336
345
|
# Oddly enough, cert-manager uses a GET request for retrieving Challenge info
|
337
|
-
challenge.client.attempt unless @
|
346
|
+
challenge.client.attempt unless @json_body && @json_body[:payload] == ""
|
338
347
|
|
339
348
|
data = {
|
340
349
|
type: challenge.acme_type,
|
@@ -344,25 +353,32 @@ module Bullion
|
|
344
353
|
url: uri("/challenges/#{challenge.id}")
|
345
354
|
}
|
346
355
|
|
347
|
-
|
356
|
+
if challenge.status == "valid"
|
357
|
+
data[:validated] = challenge.validated
|
358
|
+
order = challenge.authorization.order
|
359
|
+
order.update!(status: "ready") unless order.status == "ready"
|
360
|
+
end
|
348
361
|
|
349
362
|
data.to_json
|
363
|
+
rescue Bullion::Acme::Error => e
|
364
|
+
content_type "application/problem+json"
|
365
|
+
halt 422, { type: e.acme_error, detail: e.message }.to_json
|
350
366
|
end
|
351
367
|
|
352
368
|
# Retrieves a signed certificate
|
353
369
|
# @see https://tools.ietf.org/html/rfc8555#section-7.4.2
|
354
|
-
post
|
370
|
+
post "/certificates/:id" do
|
355
371
|
parse_acme_jwt
|
356
372
|
|
357
373
|
order = Models::Order.where(certificate_id: params[:id]).first
|
358
|
-
if order && order.status ==
|
359
|
-
content_type
|
374
|
+
if order && order.status == "valid"
|
375
|
+
content_type "application/pem-certificate-chain"
|
360
376
|
|
361
377
|
cert = Models::Certificate.find(params[:id])
|
362
378
|
|
363
379
|
cert.data + Bullion.ca_cert.to_pem
|
364
380
|
else
|
365
|
-
halt(422, {
|
381
|
+
halt(422, { error: "Order not valid" }.to_json)
|
366
382
|
end
|
367
383
|
end
|
368
384
|
end
|
@@ -10,25 +10,25 @@ module Bullion
|
|
10
10
|
end
|
11
11
|
|
12
12
|
before do
|
13
|
-
content_type
|
13
|
+
content_type "application/json"
|
14
14
|
|
15
15
|
halt 403 unless request.get? || request.options?
|
16
16
|
|
17
17
|
if request.get?
|
18
|
-
headers
|
19
|
-
headers
|
18
|
+
headers "X-Frame-Options" => "SAMEORIGIN"
|
19
|
+
headers "X-XSS-Protection" => "1; mode=block"
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
23
23
|
after do
|
24
|
-
headers
|
24
|
+
headers "Access-Control-Allow-Methods" => %w[GET] if request.options?
|
25
25
|
end
|
26
26
|
|
27
|
-
get
|
27
|
+
get "/" do
|
28
28
|
'{ "status": "up" }'
|
29
29
|
end
|
30
30
|
|
31
|
-
options
|
31
|
+
options "/" do
|
32
32
|
halt 200
|
33
33
|
end
|
34
34
|
end
|
data/lib/bullion/version.rb
CHANGED
data/lib/bullion.rb
CHANGED
@@ -1,21 +1,21 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# Standard Library requirements
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require
|
8
|
-
require
|
9
|
-
require
|
4
|
+
require "base64"
|
5
|
+
require "resolv"
|
6
|
+
require "securerandom"
|
7
|
+
require "time"
|
8
|
+
require "logger"
|
9
|
+
require "openssl"
|
10
10
|
|
11
11
|
# External requirements
|
12
|
-
require
|
13
|
-
require
|
14
|
-
require
|
15
|
-
require
|
16
|
-
require
|
17
|
-
require
|
18
|
-
require
|
12
|
+
require "sinatra/base"
|
13
|
+
require "sinatra/custom_logger"
|
14
|
+
require "mysql2"
|
15
|
+
require "sinatra/activerecord"
|
16
|
+
require "jwt"
|
17
|
+
require "prometheus/client"
|
18
|
+
require "httparty"
|
19
19
|
|
20
20
|
# The top-level module for Bullion
|
21
21
|
module Bullion
|
@@ -25,33 +25,35 @@ module Bullion
|
|
25
25
|
LOGGER = Logger.new($stdout)
|
26
26
|
|
27
27
|
# Config through environment variables
|
28
|
-
CA_DIR = File.expand_path ENV.fetch(
|
29
|
-
CA_SECRET = ENV.fetch(
|
30
|
-
CA_KEY_PATH = ENV.fetch(
|
31
|
-
CA_CERT_PATH = ENV.fetch(
|
32
|
-
CA_DOMAINS = ENV.fetch(
|
28
|
+
CA_DIR = File.expand_path ENV.fetch("CA_DIR", "tmp")
|
29
|
+
CA_SECRET = ENV.fetch("CA_SECRET", "SomeS3cret")
|
30
|
+
CA_KEY_PATH = ENV.fetch("CA_KEY_PATH") { File.join(CA_DIR, "tls.key") }
|
31
|
+
CA_CERT_PATH = ENV.fetch("CA_CERT_PATH") { File.join(CA_DIR, "tls.crt") }
|
32
|
+
CA_DOMAINS = ENV.fetch("CA_DOMAINS", "example.com").split(",")
|
33
33
|
|
34
34
|
# Set up log level
|
35
|
-
LOGGER.level = ENV.fetch(
|
35
|
+
LOGGER.level = ENV.fetch("LOG_LEVEL", :warn)
|
36
36
|
|
37
37
|
# 90 days cert expiration
|
38
38
|
CERT_VALIDITY_DURATION = Integer(
|
39
|
-
ENV.fetch(
|
39
|
+
ENV.fetch("CERT_VALIDITY_DURATION", 60 * 60 * 24 * 30 * 3)
|
40
40
|
)
|
41
41
|
|
42
42
|
DB_CONNECTION_SETTINGS =
|
43
|
-
ENV
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
43
|
+
ENV.fetch("DATABASE_URL") do
|
44
|
+
{
|
45
|
+
adapter: "mysql2",
|
46
|
+
database: ENV.fetch("DB_NAME", "bullion"),
|
47
|
+
encoding: ENV.fetch("DB_ENCODING", "utf8mb4"),
|
48
|
+
pool: Integer(ENV.fetch("MAX_THREADS", 32)),
|
49
|
+
username: ENV.fetch("DB_USERNAME", "root"),
|
50
|
+
password: ENV.fetch("DB_PASSWORD", nil),
|
51
|
+
host: ENV.fetch("DB_HOST", "localhost")
|
52
|
+
}
|
53
|
+
end
|
52
54
|
DB_CONNECTION_SETTINGS.freeze
|
53
55
|
|
54
|
-
NAMESERVERS = ENV.fetch(
|
56
|
+
NAMESERVERS = ENV.fetch("DNS01_NAMESERVERS", "").split(",")
|
55
57
|
|
56
58
|
MetricsRegistry = Prometheus::Client.registry
|
57
59
|
|
@@ -74,24 +76,35 @@ module Bullion
|
|
74
76
|
# Ensures configuration settings are valid
|
75
77
|
# @see https://support.apple.com/en-us/HT211025
|
76
78
|
def self.validate_config!
|
77
|
-
raise ConfigError,
|
79
|
+
raise ConfigError, "Invalid Key Passphrase" unless CA_SECRET.is_a?(String)
|
78
80
|
raise ConfigError, "Invalid Key Path: #{CA_KEY_PATH}" unless File.readable?(CA_KEY_PATH)
|
79
81
|
raise ConfigError, "Invalid Cert Path: #{CA_CERT_PATH}" unless File.readable?(CA_CERT_PATH)
|
80
|
-
raise ConfigError,
|
81
|
-
raise ConfigError,
|
82
|
+
raise ConfigError, "Cert Validity Too Long" if CERT_VALIDITY_DURATION > 60 * 60 * 24 * 397
|
83
|
+
raise ConfigError, "Cert Validity Too Short" if CERT_VALIDITY_DURATION < 60 * 60 * 24 * 2
|
82
84
|
end
|
83
85
|
end
|
84
86
|
|
85
87
|
# Internal requirements
|
86
|
-
require
|
87
|
-
require
|
88
|
-
require
|
89
|
-
require
|
90
|
-
require
|
91
|
-
require
|
92
|
-
require
|
93
|
-
require
|
94
|
-
require
|
95
|
-
require
|
96
|
-
require
|
97
|
-
require
|
88
|
+
require "bullion/version"
|
89
|
+
require "bullion/acme/error"
|
90
|
+
require "bullion/helpers/acme"
|
91
|
+
require "bullion/helpers/service"
|
92
|
+
require "bullion/helpers/ssl"
|
93
|
+
require "bullion/models"
|
94
|
+
require "bullion/service"
|
95
|
+
require "bullion/services/ping"
|
96
|
+
require "bullion/services/ca"
|
97
|
+
require "bullion/challenge_client"
|
98
|
+
require "bullion/challenge_clients/dns"
|
99
|
+
require "bullion/challenge_clients/http"
|
100
|
+
|
101
|
+
if %w[development test].include?(ENV["RACK_ENV"])
|
102
|
+
require "bullion/rspec/challenge_clients/dns"
|
103
|
+
require "bullion/rspec/challenge_clients/http"
|
104
|
+
|
105
|
+
Bullion::DNS_CHALLENGE_CLIENT = Bullion::RSpec::ChallengeClients::DNS
|
106
|
+
Bullion::HTTP_CHALLENGE_CLIENT = Bullion::RSpec::ChallengeClients::HTTP
|
107
|
+
else
|
108
|
+
Bullion::DNS_CHALLENGE_CLIENT = Bullion::ChallengeClients::DNS
|
109
|
+
Bullion::HTTP_CHALLENGE_CLIENT = Bullion::ChallengeClients::HTTP
|
110
|
+
end
|
data/scripts/build.sh
ADDED
data/scripts/release.sh
ADDED