bullion 0.8.0 → 0.10.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/.github/workflows/ci.yml +1 -1
- data/.release-please-manifest.json +1 -1
- data/.rubocop.yml +2 -1
- data/.ruby-version +1 -1
- data/CHANGELOG.md +22 -0
- data/Dockerfile +10 -6
- data/Gemfile.lock +77 -64
- data/Itsi.rb +123 -0
- data/README.md +3 -2
- data/Rakefile +55 -55
- data/bullion.gemspec +3 -3
- data/config.ru +0 -2
- data/lib/bullion/challenge_client.rb +5 -1
- data/lib/bullion/challenge_clients/dns.rb +1 -1
- data/lib/bullion/challenge_clients/http.rb +1 -1
- data/lib/bullion/helpers/acme.rb +9 -9
- data/lib/bullion/helpers/ssl.rb +0 -5
- data/lib/bullion/models/authorization.rb +9 -0
- data/lib/bullion/models/certificate.rb +2 -0
- data/lib/bullion/models/challenge.rb +8 -1
- data/lib/bullion/models/order.rb +11 -3
- data/lib/bullion/models/order_csr.rb +36 -0
- data/lib/bullion/models.rb +3 -0
- data/lib/bullion/services/ca.rb +17 -22
- data/lib/bullion/version.rb +1 -1
- data/lib/bullion.rb +6 -6
- data/scripts/docker-entrypoint.sh +1 -5
- metadata +22 -25
- data/config/puma.rb +0 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 74fbfc1ee8b98c2ff1c707302c8e62c9b2f8d106308b8b3b02d6a1ffe622cb77
|
4
|
+
data.tar.gz: adbd79fc4e82a9630dee2dd71c1e33c7e0a8f02732c986af07b1ba3d1733fd5c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f1cfc723554109cdb6e837e2dabd66eed6dd52d3cd7ca649923f88940c31e83e957e749ec1449c6eef4ffbcc89aafbbf822a3725b55688a3dc5d4290bd63552b
|
7
|
+
data.tar.gz: ccbe2a235fa91b4568b6977f55b22122de5f0fc1e0c2620c4ef9e16dfc4ce618ccff0322ea1e5c9dc3711f2dc10b22a9dc0bcd5c45d406adb08640229ec78048
|
data/.github/workflows/ci.yml
CHANGED
data/.rubocop.yml
CHANGED
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
3.
|
1
|
+
3.4.4
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,27 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## [0.10.0](https://github.com/jgnagy/bullion/compare/bullion/v0.9.0...bullion/v0.10.0) (2025-07-05)
|
4
|
+
|
5
|
+
|
6
|
+
### ⚠ BREAKING CHANGES
|
7
|
+
|
8
|
+
* update Docker image with Itsi configuration
|
9
|
+
|
10
|
+
### Bug Fixes
|
11
|
+
|
12
|
+
* update Docker image with Itsi configuration ([4be39dd](https://github.com/jgnagy/bullion/commit/4be39dd6200f058907029e23a07f19241705b701))
|
13
|
+
|
14
|
+
## [0.9.0](https://github.com/jgnagy/bullion/compare/bullion/v0.8.0...bullion/v0.9.0) (2025-07-05)
|
15
|
+
|
16
|
+
|
17
|
+
### ⚠ BREAKING CHANGES
|
18
|
+
|
19
|
+
* full ruby and dependency upgrade
|
20
|
+
|
21
|
+
### Miscellaneous Chores
|
22
|
+
|
23
|
+
* full ruby and dependency upgrade ([7625208](https://github.com/jgnagy/bullion/commit/7625208b1c4fa6b1acb5a0c9e7362001d66e4e08))
|
24
|
+
|
3
25
|
## [0.8.0](https://github.com/jgnagy/bullion/compare/bullion-v0.7.3...bullion/v0.8.0) (2025-03-13)
|
4
26
|
|
5
27
|
|
data/Dockerfile
CHANGED
@@ -1,10 +1,10 @@
|
|
1
|
-
FROM ruby:3.
|
1
|
+
FROM ruby:3.4.4 AS build
|
2
2
|
|
3
3
|
ENV RACK_ENV=development
|
4
4
|
|
5
5
|
COPY . /build
|
6
6
|
|
7
|
-
RUN apt-get update && apt-get upgrade -y && apt-get install -y libsqlite3-dev sqlite3 curl libsodium-dev
|
7
|
+
RUN apt-get update && apt-get upgrade -y && apt-get install -y libsqlite3-dev sqlite3 curl libsodium-dev build-essential libclang-dev
|
8
8
|
|
9
9
|
RUN cd /build \
|
10
10
|
&& gem build bullion.gemspec \
|
@@ -12,14 +12,14 @@ RUN cd /build \
|
|
12
12
|
|
13
13
|
WORKDIR /build
|
14
14
|
|
15
|
-
FROM ruby:3.
|
15
|
+
FROM ruby:3.4.4
|
16
16
|
LABEL maintainer="Jonathan Gnagy <jonathan.gnagy@gmail.com>"
|
17
17
|
|
18
18
|
ENV BULLION_PORT=9292
|
19
|
-
ENV
|
19
|
+
ENV RACK_ENV=production
|
20
20
|
ENV DATABASE_URL=sqlite3:///tmp/bullion.db
|
21
21
|
|
22
|
-
RUN apt-get update && apt-get upgrade -y && apt-get -y install libsqlite3-dev sqlite3 curl libsodium-dev
|
22
|
+
RUN apt-get update && apt-get upgrade -y && apt-get -y install libsqlite3-dev sqlite3 curl libsodium-dev build-essential libclang-dev
|
23
23
|
|
24
24
|
RUN mkdir /app
|
25
25
|
|
@@ -27,6 +27,7 @@ COPY ./scripts/docker-entrypoint.sh /entrypoint.sh
|
|
27
27
|
COPY --from=build /bullion.gem /app/bullion.gem
|
28
28
|
COPY ./db /app/db
|
29
29
|
COPY ./config.ru /app/config.ru
|
30
|
+
COPY ./Itsi.rb /app/Itsi.rb
|
30
31
|
COPY ./Rakefile /app/Rakefile
|
31
32
|
|
32
33
|
RUN mkdir /ssl
|
@@ -34,7 +35,8 @@ RUN mkdir /ssl
|
|
34
35
|
RUN chmod +x /entrypoint.sh \
|
35
36
|
&& chown nobody /app/db \
|
36
37
|
&& chown nobody /app/db/schema.rb \
|
37
|
-
&& chown -R nobody:nogroup /ssl
|
38
|
+
&& chown -R nobody:nogroup /ssl \
|
39
|
+
&& chown nobody /app
|
38
40
|
|
39
41
|
WORKDIR /app
|
40
42
|
|
@@ -42,4 +44,6 @@ RUN gem install bullion.gem
|
|
42
44
|
|
43
45
|
USER nobody
|
44
46
|
|
47
|
+
EXPOSE 9292
|
48
|
+
|
45
49
|
ENTRYPOINT ["/entrypoint.sh"]
|
data/Gemfile.lock
CHANGED
@@ -1,26 +1,26 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
bullion (0.
|
4
|
+
bullion (0.10.0)
|
5
5
|
benchmark (~> 0.4)
|
6
6
|
dry-configurable (~> 1.1)
|
7
7
|
httparty (~> 0.21)
|
8
|
+
itsi (~> 0.2)
|
8
9
|
json (~> 2.6)
|
9
10
|
jwt (~> 2.7)
|
10
11
|
mysql2 (~> 0.5)
|
11
12
|
openssl (~> 3.0)
|
12
13
|
prometheus-client (~> 4.2)
|
13
|
-
puma (~> 6.4)
|
14
14
|
sinatra (~> 3.1)
|
15
15
|
sinatra-activerecord (~> 2.0)
|
16
16
|
sinatra-contrib (~> 3.1)
|
17
|
-
sqlite3 (~> 2.
|
17
|
+
sqlite3 (~> 2.7)
|
18
18
|
|
19
19
|
GEM
|
20
20
|
remote: https://rubygems.org/
|
21
21
|
specs:
|
22
|
-
acme-client (2.0.
|
23
|
-
base64 (~> 0.2
|
22
|
+
acme-client (2.0.22)
|
23
|
+
base64 (~> 0.2)
|
24
24
|
faraday (>= 1.0, < 3.0.0)
|
25
25
|
faraday-retry (>= 1.0, < 3.0.0)
|
26
26
|
activemodel (8.0.2)
|
@@ -42,18 +42,18 @@ GEM
|
|
42
42
|
securerandom (>= 0.3)
|
43
43
|
tzinfo (~> 2.0, >= 2.0.5)
|
44
44
|
uri (>= 0.13.1)
|
45
|
-
ast (2.4.
|
45
|
+
ast (2.4.3)
|
46
46
|
backport (1.2.0)
|
47
|
-
base64 (0.
|
48
|
-
benchmark (0.4.
|
49
|
-
bigdecimal (3.
|
47
|
+
base64 (0.3.0)
|
48
|
+
benchmark (0.4.1)
|
49
|
+
bigdecimal (3.2.2)
|
50
50
|
byebug (11.1.3)
|
51
51
|
concurrent-ruby (1.3.5)
|
52
|
-
connection_pool (2.5.
|
53
|
-
csv (3.3.
|
54
|
-
diff-lcs (1.6.
|
52
|
+
connection_pool (2.5.3)
|
53
|
+
csv (3.3.5)
|
54
|
+
diff-lcs (1.6.2)
|
55
55
|
docile (1.4.1)
|
56
|
-
drb (2.2.
|
56
|
+
drb (2.2.3)
|
57
57
|
dry-configurable (1.3.0)
|
58
58
|
dry-core (~> 1.1)
|
59
59
|
zeitwerk (~> 2.6)
|
@@ -61,98 +61,109 @@ GEM
|
|
61
61
|
concurrent-ruby (~> 1.0)
|
62
62
|
logger
|
63
63
|
zeitwerk (~> 2.6)
|
64
|
-
faraday (2.
|
64
|
+
faraday (2.13.2)
|
65
65
|
faraday-net_http (>= 2.0, < 3.5)
|
66
66
|
json
|
67
67
|
logger
|
68
|
-
faraday-net_http (3.4.
|
68
|
+
faraday-net_http (3.4.1)
|
69
69
|
net-http (>= 0.5.0)
|
70
|
-
faraday-retry (2.2
|
70
|
+
faraday-retry (2.3.2)
|
71
71
|
faraday (~> 2.0)
|
72
|
-
httparty (0.
|
72
|
+
httparty (0.23.1)
|
73
73
|
csv
|
74
74
|
mini_mime (>= 1.0.0)
|
75
75
|
multi_xml (>= 0.5.2)
|
76
76
|
i18n (1.14.7)
|
77
77
|
concurrent-ruby (~> 1.0)
|
78
|
-
|
79
|
-
|
80
|
-
|
78
|
+
itsi (0.2.18)
|
79
|
+
itsi-scheduler (~> 0.2.18)
|
80
|
+
itsi-server (~> 0.2.18)
|
81
|
+
itsi-scheduler (0.2.18)
|
82
|
+
rb_sys (~> 0.9.91)
|
83
|
+
itsi-server (0.2.18)
|
84
|
+
json (~> 2)
|
85
|
+
prism (~> 1.4)
|
86
|
+
rack (>= 1.6)
|
87
|
+
rb_sys (~> 0.9.91)
|
88
|
+
jaro_winkler (1.6.1)
|
89
|
+
json (2.12.2)
|
90
|
+
jwt (2.10.2)
|
81
91
|
base64
|
82
92
|
kramdown (2.5.1)
|
83
93
|
rexml (>= 3.3.9)
|
84
94
|
kramdown-parser-gfm (1.1.0)
|
85
95
|
kramdown (~> 2.0)
|
86
|
-
language_server-protocol (3.17.0.
|
96
|
+
language_server-protocol (3.17.0.5)
|
87
97
|
lint_roller (1.1.0)
|
88
|
-
logger (1.
|
98
|
+
logger (1.7.0)
|
89
99
|
mini_mime (1.1.5)
|
90
100
|
minitest (5.25.5)
|
91
101
|
multi_json (1.15.0)
|
92
|
-
multi_xml (0.7.
|
102
|
+
multi_xml (0.7.2)
|
93
103
|
bigdecimal (~> 3.1)
|
94
104
|
mustermann (3.0.3)
|
95
105
|
ruby2_keywords (~> 0.0.1)
|
96
106
|
mysql2 (0.5.6)
|
97
107
|
net-http (0.6.0)
|
98
108
|
uri
|
99
|
-
|
100
|
-
nokogiri (1.18.3-aarch64-linux-gnu)
|
109
|
+
nokogiri (1.18.8-aarch64-linux-gnu)
|
101
110
|
racc (~> 1.4)
|
102
|
-
nokogiri (1.18.
|
111
|
+
nokogiri (1.18.8-aarch64-linux-musl)
|
103
112
|
racc (~> 1.4)
|
104
|
-
nokogiri (1.18.
|
113
|
+
nokogiri (1.18.8-arm-linux-gnu)
|
105
114
|
racc (~> 1.4)
|
106
|
-
nokogiri (1.18.
|
115
|
+
nokogiri (1.18.8-arm-linux-musl)
|
107
116
|
racc (~> 1.4)
|
108
|
-
nokogiri (1.18.
|
117
|
+
nokogiri (1.18.8-arm64-darwin)
|
109
118
|
racc (~> 1.4)
|
110
|
-
nokogiri (1.18.
|
119
|
+
nokogiri (1.18.8-x86_64-darwin)
|
111
120
|
racc (~> 1.4)
|
112
|
-
nokogiri (1.18.
|
121
|
+
nokogiri (1.18.8-x86_64-linux-gnu)
|
113
122
|
racc (~> 1.4)
|
114
|
-
nokogiri (1.18.
|
123
|
+
nokogiri (1.18.8-x86_64-linux-musl)
|
115
124
|
racc (~> 1.4)
|
116
125
|
observer (0.1.2)
|
117
126
|
openssl (3.3.0)
|
118
|
-
ostruct (0.6.
|
119
|
-
parallel (1.
|
120
|
-
parser (3.3.
|
127
|
+
ostruct (0.6.2)
|
128
|
+
parallel (1.27.0)
|
129
|
+
parser (3.3.8.0)
|
121
130
|
ast (~> 2.4.1)
|
122
131
|
racc
|
132
|
+
prism (1.4.0)
|
123
133
|
prometheus-client (4.2.4)
|
124
134
|
base64
|
125
|
-
puma (6.6.0)
|
126
|
-
nio4r (~> 2.0)
|
127
135
|
racc (1.8.1)
|
128
|
-
rack (2.2.
|
136
|
+
rack (2.2.17)
|
129
137
|
rack-protection (3.2.0)
|
130
138
|
base64 (>= 0.1.0)
|
131
139
|
rack (~> 2.2, >= 2.2.4)
|
132
140
|
rack-test (2.2.0)
|
133
141
|
rack (>= 1.3)
|
134
142
|
rainbow (3.1.1)
|
135
|
-
rake (13.
|
136
|
-
|
143
|
+
rake (13.3.0)
|
144
|
+
rake-compiler-dock (1.9.1)
|
145
|
+
rb_sys (0.9.116)
|
146
|
+
rake-compiler-dock (= 1.9.1)
|
147
|
+
rbs (3.9.4)
|
137
148
|
logger
|
138
149
|
regexp_parser (2.10.0)
|
139
150
|
reverse_markdown (3.0.0)
|
140
151
|
nokogiri
|
141
152
|
rexml (3.4.1)
|
142
|
-
rspec (3.13.
|
153
|
+
rspec (3.13.1)
|
143
154
|
rspec-core (~> 3.13.0)
|
144
155
|
rspec-expectations (~> 3.13.0)
|
145
156
|
rspec-mocks (~> 3.13.0)
|
146
|
-
rspec-core (3.13.
|
157
|
+
rspec-core (3.13.5)
|
147
158
|
rspec-support (~> 3.13.0)
|
148
|
-
rspec-expectations (3.13.
|
159
|
+
rspec-expectations (3.13.5)
|
149
160
|
diff-lcs (>= 1.2.0, < 2.0)
|
150
161
|
rspec-support (~> 3.13.0)
|
151
|
-
rspec-mocks (3.13.
|
162
|
+
rspec-mocks (3.13.5)
|
152
163
|
diff-lcs (>= 1.2.0, < 2.0)
|
153
164
|
rspec-support (~> 3.13.0)
|
154
|
-
rspec-support (3.13.
|
155
|
-
rubocop (1.
|
165
|
+
rspec-support (3.13.4)
|
166
|
+
rubocop (1.77.0)
|
156
167
|
json (~> 2.3)
|
157
168
|
language_server-protocol (~> 3.17.0.2)
|
158
169
|
lint_roller (~> 1.1.0)
|
@@ -160,15 +171,16 @@ GEM
|
|
160
171
|
parser (>= 3.3.0.2)
|
161
172
|
rainbow (>= 2.2.2, < 4.0)
|
162
173
|
regexp_parser (>= 2.9.3, < 3.0)
|
163
|
-
rubocop-ast (>= 1.
|
174
|
+
rubocop-ast (>= 1.45.1, < 2.0)
|
164
175
|
ruby-progressbar (~> 1.7)
|
165
176
|
unicode-display_width (>= 2.4.0, < 4.0)
|
166
|
-
rubocop-ast (1.
|
167
|
-
parser (>= 3.3.
|
177
|
+
rubocop-ast (1.45.1)
|
178
|
+
parser (>= 3.3.7.2)
|
179
|
+
prism (~> 1.4)
|
168
180
|
rubocop-rake (0.7.1)
|
169
181
|
lint_roller (~> 1.1)
|
170
182
|
rubocop (>= 1.72.1)
|
171
|
-
rubocop-rspec (3.
|
183
|
+
rubocop-rspec (3.6.0)
|
172
184
|
lint_roller (~> 1.1)
|
173
185
|
rubocop (~> 1.72, >= 1.72.1)
|
174
186
|
ruby-progressbar (1.13.0)
|
@@ -197,33 +209,34 @@ GEM
|
|
197
209
|
rack-protection (= 3.2.0)
|
198
210
|
sinatra (= 3.2.0)
|
199
211
|
tilt (~> 2.0)
|
200
|
-
solargraph (0.
|
212
|
+
solargraph (0.56.0)
|
201
213
|
backport (~> 1.2)
|
202
|
-
benchmark
|
214
|
+
benchmark (~> 0.4)
|
203
215
|
bundler (~> 2.0)
|
204
216
|
diff-lcs (~> 1.4)
|
205
|
-
jaro_winkler (~> 1.6)
|
217
|
+
jaro_winkler (~> 1.6, >= 1.6.1)
|
206
218
|
kramdown (~> 2.3)
|
207
219
|
kramdown-parser-gfm (~> 1.1)
|
208
220
|
logger (~> 1.6)
|
209
221
|
observer (~> 0.1)
|
210
222
|
ostruct (~> 0.6)
|
211
223
|
parser (~> 3.0)
|
212
|
-
|
213
|
-
|
224
|
+
prism (~> 1.4)
|
225
|
+
rbs (~> 3.3)
|
226
|
+
reverse_markdown (~> 3.0)
|
214
227
|
rubocop (~> 1.38)
|
215
228
|
thor (~> 1.0)
|
216
229
|
tilt (~> 2.0)
|
217
230
|
yard (~> 0.9, >= 0.9.24)
|
218
231
|
yard-solargraph (~> 0.1)
|
219
|
-
sqlite3 (2.
|
220
|
-
sqlite3 (2.
|
221
|
-
sqlite3 (2.
|
222
|
-
sqlite3 (2.
|
223
|
-
sqlite3 (2.
|
224
|
-
sqlite3 (2.
|
225
|
-
sqlite3 (2.
|
226
|
-
sqlite3 (2.
|
232
|
+
sqlite3 (2.7.1-aarch64-linux-gnu)
|
233
|
+
sqlite3 (2.7.1-aarch64-linux-musl)
|
234
|
+
sqlite3 (2.7.1-arm-linux-gnu)
|
235
|
+
sqlite3 (2.7.1-arm-linux-musl)
|
236
|
+
sqlite3 (2.7.1-arm64-darwin)
|
237
|
+
sqlite3 (2.7.1-x86_64-darwin)
|
238
|
+
sqlite3 (2.7.1-x86_64-linux-gnu)
|
239
|
+
sqlite3 (2.7.1-x86_64-linux-musl)
|
227
240
|
thor (1.3.2)
|
228
241
|
tilt (2.6.0)
|
229
242
|
timeout (0.4.3)
|
@@ -236,7 +249,7 @@ GEM
|
|
236
249
|
yard (0.9.37)
|
237
250
|
yard-solargraph (0.1.0)
|
238
251
|
yard (~> 0.9)
|
239
|
-
zeitwerk (2.7.
|
252
|
+
zeitwerk (2.7.3)
|
240
253
|
|
241
254
|
PLATFORMS
|
242
255
|
aarch64-linux
|
data/Itsi.rb
ADDED
@@ -0,0 +1,123 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# This is the default Itsi configuration file, installed when you run `itsi init`
|
4
|
+
# It contains a sane starting point for configuring your Itsi server.
|
5
|
+
# You can use this file in both development and production environments.
|
6
|
+
# Most of the options in this file can be overridden by command line options.
|
7
|
+
# Check out itsi -h to learn more about the command line options available to you.
|
8
|
+
|
9
|
+
env = ENV.fetch("APP_ENV") { ENV.fetch("RACK_ENV", "development") }
|
10
|
+
|
11
|
+
# Number of worker processes to spawn
|
12
|
+
# If more than 1, Itsi will be booted in Cluster mode
|
13
|
+
workers ENV["WORKERS"]&.to_i || (env == "development" ? 1 : nil)
|
14
|
+
|
15
|
+
# Number of threads to spawn per worker process
|
16
|
+
# For pure CPU bound applicationss, you'll get the best results keeping this number low
|
17
|
+
# Setting a value of 1 is great for superficial benchmarks, but in reality
|
18
|
+
# it's better to set this a bit higher to allow expensive requests to get overtaken and minimize head-of-line blocking
|
19
|
+
threads ENV.fetch("THREADS", 3).to_i
|
20
|
+
|
21
|
+
# If your application is IO bound (e.g. performing a lot of proxied HTTP requests, or heavy queries etc)
|
22
|
+
# you can see *substantial* benefits from enabling this option.
|
23
|
+
# To set this option, pass a string, not a class (as we will not have loaded the class yet)
|
24
|
+
# E.g.
|
25
|
+
# `fiber_scheduler "Itsi::Scheduler"` - The default fast and light-weight scheduler that comes with Itsi
|
26
|
+
# `fiber_scheduler "Async::Scheduler"` - Bring your own scheduler!
|
27
|
+
fiber_scheduler "Itsi::Scheduler"
|
28
|
+
|
29
|
+
# If you bind to https, without specifying a certificate, Itsi will use a self-signed certificate.
|
30
|
+
# The self-signed certificate will use a CA generated for your
|
31
|
+
# host and stored inside `ITSI_LOCAL_CA_DIR` (Defaults to ~/.itsi)
|
32
|
+
# bind "https://0.0.0.0:3000"
|
33
|
+
# bind "https://0.0.0.0:3000?domains=dev.itsi.fyi"
|
34
|
+
#
|
35
|
+
# If you want to use let's encrypt to generate you a real certificate you
|
36
|
+
# and pass cert=acme and an acme_email address to generate one.
|
37
|
+
# bind "https://itsi.fyi?cert=acme&acme_email=admin@itsi.fyi"
|
38
|
+
# You can generate certificates for multiple domains at once, by passing a comma-separated list of domains
|
39
|
+
# bind "https://0.0.0.0?domains=foo.itsi.fyi,bar.itsi.fyi&cert=acme&acme_email=admin@itsi.fyi"
|
40
|
+
#
|
41
|
+
# If you already have a certificate you can specify it using the cert and key parameters
|
42
|
+
# bind "https://itsi.fyi?cert=/path/to/cert.pem&key=/path/to/key.pem"
|
43
|
+
#
|
44
|
+
# You can also bind to a unix socket or a tls unix socket. E.g.
|
45
|
+
# bind "unix:///tmp/itsi.sock"
|
46
|
+
# bind "tls:///tmp/itsi.secure.sock"
|
47
|
+
|
48
|
+
bind "http://0.0.0.0:#{ENV.fetch("BULLION_PORT", 9292)}"
|
49
|
+
|
50
|
+
# If you want to preload the application, set preload to true
|
51
|
+
# to load the entire rack-app defined in rack_file_name before forking.
|
52
|
+
# Alternatively, you can preload just a specific set of gems in a group in your gemfile,
|
53
|
+
# by providing the group name here.
|
54
|
+
# E.g.
|
55
|
+
#
|
56
|
+
# preload :preload # Load gems inside the preload group
|
57
|
+
# preload false # Don't preload.
|
58
|
+
#
|
59
|
+
# If you want to be able to perform zero-downtime deploys using a single itsi process,
|
60
|
+
# you should disable preloads, so that the application is loaded fresh each time a new worker boots
|
61
|
+
preload true
|
62
|
+
|
63
|
+
# Set the maximum memory limit for each worker process in bytes
|
64
|
+
# When this limit is reached, the worker will be gracefully restarted.
|
65
|
+
# Only one worker is restarted at a time to ensure we don't take down
|
66
|
+
# all of them at once, if they reach the threshold simultaneously.
|
67
|
+
worker_memory_limit ENV.fetch("WORKER_MEMORY_LIMIT") { 1024 * 1024 * 1024 } # Default to 1GB
|
68
|
+
|
69
|
+
# You can provide an optional block of code to run, when a worker hits its memory threshold
|
70
|
+
# (Use this to send yourself an alert,
|
71
|
+
# write metrics to disk etc. etc.)
|
72
|
+
after_memory_limit_reached do |pid|
|
73
|
+
puts "Worker #{pid} has reached its memory threshold and will restart"
|
74
|
+
end
|
75
|
+
|
76
|
+
# Do clean up of any non-threadsafe resources before forking a new worker here.
|
77
|
+
before_fork {}
|
78
|
+
|
79
|
+
# Reinitialize any non-threadsafe resources after forking a new worker here.
|
80
|
+
after_fork {}
|
81
|
+
|
82
|
+
# Shutdown timeout
|
83
|
+
# Number of seconds to wait for workers to gracefully shutdown before killing them.
|
84
|
+
shutdown_timeout 5
|
85
|
+
|
86
|
+
# Set this to false for application environments that require rack.input to be a rewindable body
|
87
|
+
# (like Rails). For rack applications that can stream inputs, you can set this to true for a more
|
88
|
+
# memory-efficient approach.
|
89
|
+
stream_body false
|
90
|
+
|
91
|
+
# OOB GC responses threshold
|
92
|
+
# Specifies how frequently OOB gc should be triggered during periods where there is a gap in queued requests.
|
93
|
+
# Setting this too low can substantially worsen performance
|
94
|
+
oob_gc_responses_threshold 512
|
95
|
+
|
96
|
+
# Log level
|
97
|
+
# Set this to one of the following values: debug, info, warn, error, fatal
|
98
|
+
# Can also be set using the ITSI_LOG environment variable
|
99
|
+
log_level ENV.fetch("LOG_LEVEL", "warn").to_sym
|
100
|
+
|
101
|
+
# Log Format
|
102
|
+
# Set this to be either :plain or :json. If you leave it blank Itsi will try
|
103
|
+
# and auto-detect the format based on the TTY environment.
|
104
|
+
log_format :plain
|
105
|
+
# You can mount several Ruby apps as either
|
106
|
+
# 1. rackup files
|
107
|
+
# 2. inline rack apps
|
108
|
+
# 3. inline Ruby endpoints
|
109
|
+
#
|
110
|
+
# 1. rackup_file
|
111
|
+
rackup_file "./config.ru"
|
112
|
+
#
|
113
|
+
# 2. inline rack app
|
114
|
+
# require 'rack'
|
115
|
+
# run(Rack::Builder.app do
|
116
|
+
# use Rack::CommonLogger
|
117
|
+
# run ->(env) { [200, { 'content-type' => 'text/plain' }, ['OK']] }
|
118
|
+
# end)
|
119
|
+
#
|
120
|
+
# 3. Endpoints
|
121
|
+
# endpoint "/" do |req|
|
122
|
+
# req.ok "Hello from Itsi"
|
123
|
+
# end
|
data/README.md
CHANGED
@@ -40,8 +40,9 @@ Whether run locally or via Docker, the following environment variables configure
|
|
40
40
|
| `DNS01_NAMESERVERS` | _None_ | A comma-delimited list of nameservers to use for resolving [DNS-01](https://letsencrypt.org/docs/challenge-types/#dns-01-challenge) challenges. Usually you'll want this to be set to your _internal_ nameservers so internal names resolve correctly. When not set, it'll use the host's DNS. |
|
41
41
|
| `LOG_LEVEL` | `warn` | Log level for Bullion. Supported levels (starting with the noisiest) are debug, info, warn, error, and fatal. |
|
42
42
|
| `BULLION_PORT` | `9292` | TCP port Bullion will listen on. |
|
43
|
-
| `
|
44
|
-
| `
|
43
|
+
| `THREADS` | `3` | Number of [Itsi threads](https://itsi.fyi/options/threads/) for processing requests. |
|
44
|
+
| `WORKERS` | `1` | Number of [Itsi workers](https://itsi.fyi/options/workers/) to spawn. |
|
45
|
+
| `WORKER_MEMORY_LIMIT` | `1024**3` | [Itsi worker memory limit](https://itsi.fyi/options/worker_memory_limit/) for each worker process (in bytes). Default is 1GiB. |
|
45
46
|
| `RACK_ENV` | `production`* | When run via Docker, the default is `production`, when run via `rake local_demo` it is `development`. Used to tell Bullion if it is run in development mode or for testing. |
|
46
47
|
|
47
48
|
### Integrating
|
data/Rakefile
CHANGED
@@ -4,12 +4,62 @@ ENV["RACK_ENV"] ||= "development"
|
|
4
4
|
|
5
5
|
if %w[development test].include? ENV["RACK_ENV"]
|
6
6
|
ENV["DATABASE_URL"] = "sqlite3:#{File.expand_path(".")}/tmp/db/#{ENV["RACK_ENV"]}.sqlite3"
|
7
|
+
require "bundler/gem_tasks"
|
8
|
+
require "rspec/core/rake_task"
|
9
|
+
require "rubocop/rake_task"
|
10
|
+
require "yard"
|
11
|
+
|
12
|
+
RSpec::Core::RakeTask.new(:spec) do |t|
|
13
|
+
t.exclude_pattern = "spec/integration/**{,/*/**}/*_spec.rb"
|
14
|
+
t.rspec_opts = "--require spec_helper"
|
15
|
+
end
|
16
|
+
RSpec::Core::RakeTask.new(:integration_testing) do |t|
|
17
|
+
t.pattern = "spec/integration/**{,/*/**}/*_spec.rb"
|
18
|
+
t.rspec_opts = "--require integration_helper"
|
19
|
+
end
|
20
|
+
RuboCop::RakeTask.new(:rubocop)
|
21
|
+
YARD::Rake::YardocTask.new
|
22
|
+
|
23
|
+
desc "Runs a backgrounded demo environment"
|
24
|
+
task :demo do
|
25
|
+
rack_env = "test"
|
26
|
+
database_url = "sqlite3:#{File.expand_path(".")}/tmp/db/#{rack_env}.sqlite3"
|
27
|
+
system("RACK_ENV=\"#{rack_env}\" DATABASE_URL=\"#{database_url}\" bundle exec rake db:migrate")
|
28
|
+
system(
|
29
|
+
"RACK_ENV=\"#{rack_env}\" DATABASE_URL=\"#{database_url}\" " \
|
30
|
+
"LOG_LEVEL='#{ENV.fetch("LOG_LEVEL", "info")}' " \
|
31
|
+
"itsi --daemonize"
|
32
|
+
)
|
33
|
+
FileUtils.touch(File.join(File.expand_path("."), "tmp", "daemon.pid"))
|
34
|
+
end
|
35
|
+
|
36
|
+
desc "Runs a foregrounded demo environment"
|
37
|
+
task :foreground_demo do
|
38
|
+
system("itsi")
|
39
|
+
end
|
40
|
+
|
41
|
+
desc "Cleans up test or demo environment"
|
42
|
+
task :cleanup do
|
43
|
+
at_exit do
|
44
|
+
pid_file = File.join(File.expand_path("."), "tmp", "daemon.pid")
|
45
|
+
if File.exist?(pid_file)
|
46
|
+
system("itsi stop")
|
47
|
+
FileUtils.rm_f(pid_file)
|
48
|
+
end
|
49
|
+
FileUtils.rm_f(File.join(File.expand_path("."), "tmp", "tls.crt"))
|
50
|
+
FileUtils.rm_f(File.join(File.expand_path("."), "tmp", "tls.key"))
|
51
|
+
FileUtils.rm_f(File.join(File.expand_path("."), "tmp", "root_tls.crt"))
|
52
|
+
FileUtils.rm_f(File.join(File.expand_path("."), "tmp", "root_tls.key"))
|
53
|
+
FileUtils.rm_rf(File.join(File.expand_path("."), "tmp", "db"))
|
54
|
+
ENV["CA_DIR"] = nil
|
55
|
+
ENV["CA_SECRET"] = nil
|
56
|
+
ENV["CA_DOMAINS"] = nil
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
Rake::Task["integration_testing"].enhance(["cleanup"])
|
7
61
|
end
|
8
62
|
|
9
|
-
require "bundler/gem_tasks"
|
10
|
-
require "rspec/core/rake_task"
|
11
|
-
require "rubocop/rake_task"
|
12
|
-
require "yard"
|
13
63
|
require "openssl"
|
14
64
|
require "sqlite3"
|
15
65
|
require "sinatra/activerecord/rake"
|
@@ -22,17 +72,6 @@ namespace :db do
|
|
22
72
|
end
|
23
73
|
end
|
24
74
|
|
25
|
-
RSpec::Core::RakeTask.new(:spec) do |t|
|
26
|
-
t.exclude_pattern = "spec/integration/**{,/*/**}/*_spec.rb"
|
27
|
-
t.rspec_opts = "--require spec_helper"
|
28
|
-
end
|
29
|
-
RSpec::Core::RakeTask.new(:integration_testing) do |t|
|
30
|
-
t.pattern = "spec/integration/**{,/*/**}/*_spec.rb"
|
31
|
-
t.rspec_opts = "--require integration_helper"
|
32
|
-
end
|
33
|
-
RuboCop::RakeTask.new(:rubocop)
|
34
|
-
YARD::Rake::YardocTask.new
|
35
|
-
|
36
75
|
desc "Prepares a demo or test environment"
|
37
76
|
task :prep do
|
38
77
|
FileUtils.mkdir_p(File.join(File.expand_path("."), "tmp"))
|
@@ -48,7 +87,7 @@ task :prep do
|
|
48
87
|
root_ca.version = 2
|
49
88
|
root_ca.serial = (2**rand(10..20)) - 1
|
50
89
|
root_ca.subject = OpenSSL::X509::Name.parse(
|
51
|
-
%w[test domain].reverse.map {
|
90
|
+
%w[test domain].reverse.map { "DC=#{it}" }.join("/") + "/CN=bullion"
|
52
91
|
)
|
53
92
|
root_ca.issuer = root_ca.subject # root CA's are "self-signed"
|
54
93
|
root_ca.public_key = root_key.public_key
|
@@ -108,45 +147,6 @@ task :prep do
|
|
108
147
|
)
|
109
148
|
end
|
110
149
|
|
111
|
-
desc "Runs a backgrounded demo environment"
|
112
|
-
task :demo do
|
113
|
-
rack_env = "test"
|
114
|
-
database_url = "sqlite3:#{File.expand_path(".")}/tmp/db/#{rack_env}.sqlite3"
|
115
|
-
system("RACK_ENV=\"#{rack_env}\" DATABASE_URL=\"#{database_url}\" bundle exec rake db:migrate")
|
116
|
-
system(
|
117
|
-
"RACK_ENV=\"#{rack_env}\" DATABASE_URL=\"#{database_url}\" " \
|
118
|
-
"LOG_LEVEL='#{ENV.fetch("LOG_LEVEL", "info")}' " \
|
119
|
-
"rackup -D -P #{File.expand_path(".")}/tmp/daemon.pid"
|
120
|
-
)
|
121
|
-
end
|
122
|
-
|
123
|
-
desc "Runs a foregrounded demo environment"
|
124
|
-
task :foreground_demo do
|
125
|
-
system("rackup -o 0.0.0.0 -P #{File.expand_path(".")}/tmp/daemon.pid")
|
126
|
-
end
|
127
|
-
|
128
|
-
desc "Cleans up test or demo environment"
|
129
|
-
task :cleanup do
|
130
|
-
at_exit do
|
131
|
-
pid_file = File.join(File.expand_path("."), "tmp", "daemon.pid")
|
132
|
-
if File.exist?(pid_file)
|
133
|
-
pid = File.read(pid_file).to_i
|
134
|
-
Process.kill("TERM", pid)
|
135
|
-
FileUtils.rm_f(pid_file)
|
136
|
-
end
|
137
|
-
FileUtils.rm_f(File.join(File.expand_path("."), "tmp", "tls.crt"))
|
138
|
-
FileUtils.rm_f(File.join(File.expand_path("."), "tmp", "tls.key"))
|
139
|
-
FileUtils.rm_f(File.join(File.expand_path("."), "tmp", "root_tls.crt"))
|
140
|
-
FileUtils.rm_f(File.join(File.expand_path("."), "tmp", "root_tls.key"))
|
141
|
-
FileUtils.rm_rf(File.join(File.expand_path("."), "tmp", "db"))
|
142
|
-
ENV["CA_DIR"] = nil
|
143
|
-
ENV["CA_SECRET"] = nil
|
144
|
-
ENV["CA_DOMAINS"] = nil
|
145
|
-
end
|
146
|
-
end
|
147
|
-
|
148
|
-
Rake::Task["integration_testing"].enhance(["cleanup"])
|
149
|
-
|
150
150
|
task test: %i[prep db:migrate spec demo integration_testing]
|
151
151
|
task unit: %i[prep db:migrate spec]
|
152
152
|
|
data/bullion.gemspec
CHANGED
@@ -24,21 +24,21 @@ Gem::Specification.new do |spec|
|
|
24
24
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
25
25
|
spec.require_paths = ["lib"]
|
26
26
|
|
27
|
-
spec.required_ruby_version = "~> 3.
|
27
|
+
spec.required_ruby_version = "~> 3.4"
|
28
28
|
|
29
29
|
spec.add_dependency "benchmark", "~> 0.4"
|
30
30
|
spec.add_dependency "dry-configurable", "~> 1.1"
|
31
31
|
spec.add_dependency "httparty", "~> 0.21"
|
32
|
+
spec.add_dependency "itsi", "~> 0.2"
|
32
33
|
spec.add_dependency "json", "~> 2.6"
|
33
34
|
spec.add_dependency "jwt", "~> 2.7"
|
34
35
|
spec.add_dependency "mysql2", "~> 0.5"
|
35
36
|
spec.add_dependency "openssl", "~> 3.0"
|
36
37
|
spec.add_dependency "prometheus-client", "~> 4.2"
|
37
|
-
spec.add_dependency "puma", "~> 6.4"
|
38
38
|
spec.add_dependency "sinatra", "~> 3.1"
|
39
39
|
spec.add_dependency "sinatra-activerecord", "~> 2.0"
|
40
40
|
spec.add_dependency "sinatra-contrib", "~> 3.1"
|
41
|
-
spec.add_dependency "sqlite3", "~> 2.
|
41
|
+
spec.add_dependency "sqlite3", "~> 2.7"
|
42
42
|
|
43
43
|
spec.add_development_dependency "acme-client", "~> 2.0"
|
44
44
|
spec.add_development_dependency "bundler", "~> 2.4"
|
data/config.ru
CHANGED
@@ -27,7 +27,7 @@ module Bullion
|
|
27
27
|
benchtime = Benchmark.realtime do
|
28
28
|
until success || tries >= retries
|
29
29
|
tries += 1
|
30
|
-
success =
|
30
|
+
success = performs_challenge?
|
31
31
|
if success
|
32
32
|
LOGGER.info "Validated #{type} #{identifier}"
|
33
33
|
challenge.status = "valid"
|
@@ -59,5 +59,9 @@ module Bullion
|
|
59
59
|
def identifier
|
60
60
|
challenge.identifier
|
61
61
|
end
|
62
|
+
|
63
|
+
def performs_challenge?
|
64
|
+
raise NotImplementedError
|
65
|
+
end
|
62
66
|
end
|
63
67
|
end
|
data/lib/bullion/helpers/acme.rb
CHANGED
@@ -88,7 +88,7 @@ module Bullion
|
|
88
88
|
|
89
89
|
# Validation helpers
|
90
90
|
|
91
|
-
def
|
91
|
+
def account_data_valid?(hash)
|
92
92
|
unless [true, false, nil].include?(hash["onlyReturnExisting"])
|
93
93
|
raise Bullion::Acme::Errors::Malformed,
|
94
94
|
"Invalid onlyReturnExisting: #{hash["onlyReturnExisting"]}"
|
@@ -113,31 +113,31 @@ module Bullion
|
|
113
113
|
true
|
114
114
|
end
|
115
115
|
|
116
|
-
def
|
116
|
+
def acme_csr_valid?(order_csr)
|
117
|
+
csr = order_csr.csr
|
118
|
+
order = order_csr.order
|
117
119
|
csr_attrs = extract_csr_attrs(csr)
|
118
120
|
csr_sans = extract_csr_sans(csr_attrs)
|
119
121
|
csr_domains = extract_csr_domains(csr_sans)
|
120
122
|
csr_cn = cn_from_csr(csr)
|
121
123
|
|
122
|
-
order_domains = order.identifiers.map { |i| i["value"] }
|
123
|
-
|
124
124
|
# Make sure the CSR has a valid public key
|
125
125
|
raise Bullion::Acme::Errors::BadCsr unless csr.verify(csr.public_key)
|
126
126
|
|
127
|
-
return false unless order.
|
127
|
+
return false unless order.ready_status?
|
128
128
|
raise Bullion::Acme::Errors::BadCsr unless csr_domains.include?(csr_cn)
|
129
|
-
raise Bullion::Acme::Errors::BadCsr unless csr_domains.sort ==
|
129
|
+
raise Bullion::Acme::Errors::BadCsr unless csr_domains.sort == order.domains.sort
|
130
130
|
|
131
131
|
true
|
132
132
|
end
|
133
133
|
|
134
|
-
def
|
134
|
+
def order_valid?(hash)
|
135
135
|
validate_order_nb_and_na(hash["notBefore"], hash["notAfter"])
|
136
136
|
|
137
137
|
# Don't approve empty orders
|
138
138
|
raise Bullion::Acme::Errors::InvalidOrder, "Empty order!" if hash["identifiers"].empty?
|
139
139
|
|
140
|
-
order_domains = hash["identifiers"].select {
|
140
|
+
order_domains = hash["identifiers"].select { it["type"] == "dns" }
|
141
141
|
|
142
142
|
# Don't approve an order with identifiers that _aren't_ of type 'dns'
|
143
143
|
unless hash["identifiers"] == order_domains
|
@@ -188,7 +188,7 @@ module Bullion
|
|
188
188
|
|
189
189
|
def extract_valid_order_domains(order_domains)
|
190
190
|
order_domains.reject do |domain|
|
191
|
-
Bullion.config.ca.domains.none? { domain["value"].end_with?(
|
191
|
+
Bullion.config.ca.domains.none? { domain["value"].end_with?(it) }
|
192
192
|
end
|
193
193
|
end
|
194
194
|
end
|
data/lib/bullion/helpers/ssl.rb
CHANGED
@@ -14,11 +14,6 @@ module Bullion
|
|
14
14
|
end
|
15
15
|
end
|
16
16
|
|
17
|
-
def openssl_compat_csr(csrdata)
|
18
|
-
"-----BEGIN CERTIFICATE REQUEST-----\n" \
|
19
|
-
"#{csrdata}-----END CERTIFICATE REQUEST-----"
|
20
|
-
end
|
21
|
-
|
22
17
|
# @see https://tools.ietf.org/html/rfc7518#page-30
|
23
18
|
def key_data_to_rsa(key_data)
|
24
19
|
exponent = base64_to_long(key_data["e"])
|
@@ -13,6 +13,15 @@ module Bullion
|
|
13
13
|
|
14
14
|
validates :status, inclusion: { in: %w[invalid pending ready processing valid deactivated] }
|
15
15
|
|
16
|
+
enum :status, {
|
17
|
+
invalid: "invalid",
|
18
|
+
pending: "pending",
|
19
|
+
ready: "ready",
|
20
|
+
processing: "processing",
|
21
|
+
valid: "valid",
|
22
|
+
deactivated: "deactivated"
|
23
|
+
}, suffix: "status"
|
24
|
+
|
16
25
|
def init_values
|
17
26
|
self.expires ||= Time.now + (60 * 60)
|
18
27
|
end
|
@@ -13,6 +13,13 @@ module Bullion
|
|
13
13
|
}
|
14
14
|
validates :status, inclusion: { in: %w[invalid pending processing valid] }
|
15
15
|
|
16
|
+
enum :status, {
|
17
|
+
invalid: "invalid",
|
18
|
+
pending: "pending",
|
19
|
+
processing: "processing",
|
20
|
+
valid: "valid"
|
21
|
+
}, suffix: "status"
|
22
|
+
|
16
23
|
def identifier
|
17
24
|
authorization.identifier["value"]
|
18
25
|
end
|
@@ -29,7 +36,7 @@ module Bullion
|
|
29
36
|
end
|
30
37
|
|
31
38
|
def client
|
32
|
-
challenge_class = Bullion.acme.challenge_clients.find {
|
39
|
+
challenge_class = Bullion.acme.challenge_clients.find { it.acme_type == acme_type }
|
33
40
|
|
34
41
|
unless challenge_class
|
35
42
|
raise Bullion::Acme::Errors::UnsupportedChallengeType,
|
data/lib/bullion/models/order.rb
CHANGED
@@ -9,10 +9,19 @@ module Bullion
|
|
9
9
|
after_initialize :init_values, unless: :persisted?
|
10
10
|
|
11
11
|
belongs_to :account
|
12
|
+
belongs_to :certificate
|
12
13
|
has_many :authorizations
|
13
14
|
|
14
15
|
validates :status, inclusion: { in: %w[invalid pending ready processing valid] }
|
15
16
|
|
17
|
+
enum :status, {
|
18
|
+
invalid: "invalid",
|
19
|
+
pending: "pending",
|
20
|
+
ready: "ready",
|
21
|
+
processing: "processing",
|
22
|
+
valid: "valid"
|
23
|
+
}, suffix: "status"
|
24
|
+
|
16
25
|
def init_values
|
17
26
|
self.expires ||= Time.now + (60 * 60)
|
18
27
|
self.not_before ||= Time.now
|
@@ -31,9 +40,8 @@ module Bullion
|
|
31
40
|
end
|
32
41
|
end
|
33
42
|
|
34
|
-
|
35
|
-
|
36
|
-
end
|
43
|
+
# Used to extract domains from order (mostly for comparison with CSR)
|
44
|
+
def domains = identifiers.map { it["value"] }
|
37
45
|
end
|
38
46
|
end
|
39
47
|
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Bullion
|
4
|
+
module Models
|
5
|
+
# Pseudo-model for ACMEv2 Order CSR
|
6
|
+
class OrderCsr
|
7
|
+
class << self
|
8
|
+
def from_acme_request(order, raw_data)
|
9
|
+
decoded_data = Base64.urlsafe_decode64(raw_data)
|
10
|
+
reencoded_data = Base64.encode64(decoded_data)
|
11
|
+
csr_string = openssl_compat_csr(reencoded_data)
|
12
|
+
|
13
|
+
new(order, csr_string)
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def openssl_compat_csr(csrdata)
|
19
|
+
"-----BEGIN CERTIFICATE REQUEST-----\n" \
|
20
|
+
"#{csrdata}-----END CERTIFICATE REQUEST-----"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
attr_reader :csr, :order
|
25
|
+
|
26
|
+
def initialize(order, csr)
|
27
|
+
@order = order.is_a?(Order) ? order : Order.find(order)
|
28
|
+
@csr = if csr.is_a?(String)
|
29
|
+
OpenSSL::X509::Request.new(csr)
|
30
|
+
else
|
31
|
+
csr
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
data/lib/bullion/models.rb
CHANGED
data/lib/bullion/services/ca.rb
CHANGED
@@ -121,7 +121,7 @@ module Bullion
|
|
121
121
|
begin
|
122
122
|
parse_acme_jwt(header_data["jwk"], validate_nonce: false)
|
123
123
|
|
124
|
-
|
124
|
+
account_data_valid?(@payload_data)
|
125
125
|
rescue Bullion::Acme::Error => e
|
126
126
|
content_type "application/problem+json"
|
127
127
|
halt 400, { type: e.acme_error, detail: e.message }.to_json
|
@@ -186,7 +186,7 @@ module Bullion
|
|
186
186
|
add_acme_headers @new_nonce
|
187
187
|
|
188
188
|
{
|
189
|
-
orders: @user.orders.map {
|
189
|
+
orders: @user.orders.map { uri("/orders/#{it.id}") }
|
190
190
|
}
|
191
191
|
end
|
192
192
|
|
@@ -196,9 +196,9 @@ module Bullion
|
|
196
196
|
parse_acme_jwt
|
197
197
|
|
198
198
|
# Only identifiers of type "dns" are supported
|
199
|
-
identifiers = @payload_data["identifiers"].select {
|
199
|
+
identifiers = @payload_data["identifiers"].select { it["type"] == "dns" }
|
200
200
|
|
201
|
-
|
201
|
+
order_valid?(@payload_data)
|
202
202
|
|
203
203
|
order = @user.start_order(
|
204
204
|
identifiers:,
|
@@ -215,7 +215,7 @@ module Bullion
|
|
215
215
|
notBefore: order.not_before,
|
216
216
|
notAfter: order.not_after,
|
217
217
|
identifiers: order.identifiers,
|
218
|
-
authorizations: order.authorizations.map {
|
218
|
+
authorizations: order.authorizations.map { uri("/authorizations/#{it.id}") },
|
219
219
|
finalize: uri("/orders/#{order.id}/finalize")
|
220
220
|
}.to_json
|
221
221
|
rescue Bullion::Acme::Error => e
|
@@ -238,11 +238,11 @@ module Bullion
|
|
238
238
|
notBefore: order.not_before,
|
239
239
|
notAfter: order.not_after,
|
240
240
|
identifiers: order.identifiers,
|
241
|
-
authorizations: order.authorizations.map {
|
241
|
+
authorizations: order.authorizations.map { uri("/authorizations/#{it.id}") },
|
242
242
|
finalize: uri("/orders/#{order.id}/finalize")
|
243
243
|
}
|
244
244
|
|
245
|
-
data[:certificate] = uri("/certificates/#{order.certificate.id}") if order.
|
245
|
+
data[:certificate] = uri("/certificates/#{order.certificate.id}") if order.valid_status?
|
246
246
|
|
247
247
|
data.to_json
|
248
248
|
rescue Bullion::Acme::Error => e
|
@@ -260,14 +260,9 @@ module Bullion
|
|
260
260
|
content_type "application/json"
|
261
261
|
add_acme_headers @new_nonce, additional: { "Location" => uri("/orders/#{order.id}") }
|
262
262
|
|
263
|
-
|
264
|
-
encoded_csr = Base64.encode64(raw_csr_data)
|
263
|
+
order_csr = Models::OrderCsr.from_acme_request(order, @payload_data["csr"])
|
265
264
|
|
266
|
-
|
267
|
-
|
268
|
-
csr = OpenSSL::X509::Request.new(csr_data)
|
269
|
-
|
270
|
-
unless validate_acme_csr(order, csr)
|
265
|
+
unless acme_csr_valid?(order_csr)
|
271
266
|
content_type "application/problem+json"
|
272
267
|
halt 400, {
|
273
268
|
type: Bullion::Acme::Errors::BadCsr.new.acme_error,
|
@@ -275,7 +270,7 @@ module Bullion
|
|
275
270
|
}.to_json
|
276
271
|
end
|
277
272
|
|
278
|
-
cert_id = sign_csr(csr, @user.contacts.first).last
|
273
|
+
cert_id = sign_csr(order_csr.csr, @user.contacts.first).last
|
279
274
|
|
280
275
|
order.certificate_id = cert_id
|
281
276
|
order.status = "valid"
|
@@ -287,11 +282,11 @@ module Bullion
|
|
287
282
|
notBefore: order.not_before,
|
288
283
|
notAfter: order.not_after,
|
289
284
|
identifiers: order.identifiers,
|
290
|
-
authorizations: order.authorizations.map {
|
285
|
+
authorizations: order.authorizations.map { uri("/authorizations/#{it.id}") },
|
291
286
|
finalize: uri("/orders/#{order.id}/finalize")
|
292
287
|
}
|
293
288
|
|
294
|
-
data[:certificate] = uri("/certificates/#{order.certificate.id}") if order.
|
289
|
+
data[:certificate] = uri("/certificates/#{order.certificate.id}") if order.valid_status?
|
295
290
|
|
296
291
|
data.to_json
|
297
292
|
rescue Bullion::Acme::Error => e
|
@@ -320,7 +315,7 @@ module Bullion
|
|
320
315
|
chash[:url] = uri("/challenges/#{c.id}")
|
321
316
|
chash[:token] = c.token
|
322
317
|
chash[:status] = c.status
|
323
|
-
chash[:validated] = c.validated if c.
|
318
|
+
chash[:validated] = c.validated if c.valid_status?
|
324
319
|
|
325
320
|
chash
|
326
321
|
end
|
@@ -355,12 +350,12 @@ module Bullion
|
|
355
350
|
url: uri("/challenges/#{challenge.id}")
|
356
351
|
}
|
357
352
|
|
358
|
-
if challenge.
|
353
|
+
if challenge.valid_status?
|
359
354
|
data[:validated] = challenge.validated
|
360
355
|
authorization = challenge.authorization
|
361
|
-
authorization.update!(status: "valid") unless authorization.
|
356
|
+
authorization.update!(status: "valid") unless authorization.valid_status?
|
362
357
|
order = authorization.order
|
363
|
-
order.update!(status: "ready") unless order.
|
358
|
+
order.update!(status: "ready") unless order.ready_status?
|
364
359
|
end
|
365
360
|
|
366
361
|
add_link_relation("up", uri("/authorizations/#{challenge.authorization.id}"))
|
@@ -378,7 +373,7 @@ module Bullion
|
|
378
373
|
add_acme_headers @new_nonce
|
379
374
|
|
380
375
|
order = Models::Order.where(certificate_id: params[:id]).first
|
381
|
-
if order
|
376
|
+
if order&.valid_status?
|
382
377
|
content_type "application/pem-certificate-chain"
|
383
378
|
|
384
379
|
cert = Models::Certificate.find(params[:id])
|
data/lib/bullion/version.rb
CHANGED
data/lib/bullion.rb
CHANGED
@@ -32,7 +32,7 @@ module Bullion
|
|
32
32
|
LOGGER.level = ENV.fetch("LOG_LEVEL", :warn)
|
33
33
|
|
34
34
|
setting :ca, reader: true do
|
35
|
-
setting :dir, default: "tmp", constructor: -> { File.expand_path(
|
35
|
+
setting :dir, default: "tmp", constructor: -> { File.expand_path(it) }
|
36
36
|
setting :secret, default: "SomeS3cret"
|
37
37
|
setting(
|
38
38
|
:key_path,
|
@@ -48,22 +48,22 @@ module Bullion
|
|
48
48
|
v.include?("/") ? File.expand_path(v) : File.join(Bullion.config.ca.dir, v)
|
49
49
|
}
|
50
50
|
)
|
51
|
-
setting :domains, default: "example.com", constructor: -> {
|
51
|
+
setting :domains, default: "example.com", constructor: -> { it.split(",") }
|
52
52
|
# 90 days cert expiration
|
53
|
-
setting :cert_validity_duration, default: 60 * 60 * 24 * 30 * 3, constructor: -> { Integer(
|
53
|
+
setting :cert_validity_duration, default: 60 * 60 * 24 * 30 * 3, constructor: -> { Integer(it) }
|
54
54
|
end
|
55
55
|
|
56
56
|
setting :acme, reader: true do
|
57
57
|
setting(
|
58
58
|
:challenge_clients,
|
59
59
|
default: ["Bullion::ChallengeClients::DNS", "Bullion::ChallengeClients::HTTP"],
|
60
|
-
constructor: -> {
|
60
|
+
constructor: -> { it.map { |n| Kernel.const_get(n.to_s) } }
|
61
61
|
)
|
62
62
|
end
|
63
63
|
|
64
64
|
setting :db_url, reader: true
|
65
65
|
|
66
|
-
setting :nameservers, default: [], constructor: -> {
|
66
|
+
setting :nameservers, default: [], constructor: -> { it.split(",") }
|
67
67
|
|
68
68
|
MetricsRegistry = Prometheus::Client.registry
|
69
69
|
|
@@ -79,7 +79,7 @@ module Bullion
|
|
79
79
|
@ca_cert ||= OpenSSL::X509::Certificate.new(ca_cert_file)
|
80
80
|
end
|
81
81
|
|
82
|
-
def self.rotate_keys!
|
82
|
+
def self.rotate_keys! # rubocop:disable Naming/PredicateMethod
|
83
83
|
@ca_key = nil
|
84
84
|
@ca_cert = nil
|
85
85
|
ca_key
|
metadata
CHANGED
@@ -1,14 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bullion
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.10.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jonathan Gnagy
|
8
|
-
autorequire:
|
9
8
|
bindir: exe
|
10
9
|
cert_chain: []
|
11
|
-
date:
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
12
11
|
dependencies:
|
13
12
|
- !ruby/object:Gem::Dependency
|
14
13
|
name: benchmark
|
@@ -52,6 +51,20 @@ dependencies:
|
|
52
51
|
- - "~>"
|
53
52
|
- !ruby/object:Gem::Version
|
54
53
|
version: '0.21'
|
54
|
+
- !ruby/object:Gem::Dependency
|
55
|
+
name: itsi
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - "~>"
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '0.2'
|
61
|
+
type: :runtime
|
62
|
+
prerelease: false
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - "~>"
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '0.2'
|
55
68
|
- !ruby/object:Gem::Dependency
|
56
69
|
name: json
|
57
70
|
requirement: !ruby/object:Gem::Requirement
|
@@ -122,20 +135,6 @@ dependencies:
|
|
122
135
|
- - "~>"
|
123
136
|
- !ruby/object:Gem::Version
|
124
137
|
version: '4.2'
|
125
|
-
- !ruby/object:Gem::Dependency
|
126
|
-
name: puma
|
127
|
-
requirement: !ruby/object:Gem::Requirement
|
128
|
-
requirements:
|
129
|
-
- - "~>"
|
130
|
-
- !ruby/object:Gem::Version
|
131
|
-
version: '6.4'
|
132
|
-
type: :runtime
|
133
|
-
prerelease: false
|
134
|
-
version_requirements: !ruby/object:Gem::Requirement
|
135
|
-
requirements:
|
136
|
-
- - "~>"
|
137
|
-
- !ruby/object:Gem::Version
|
138
|
-
version: '6.4'
|
139
138
|
- !ruby/object:Gem::Dependency
|
140
139
|
name: sinatra
|
141
140
|
requirement: !ruby/object:Gem::Requirement
|
@@ -184,14 +183,14 @@ dependencies:
|
|
184
183
|
requirements:
|
185
184
|
- - "~>"
|
186
185
|
- !ruby/object:Gem::Version
|
187
|
-
version: '2.
|
186
|
+
version: '2.7'
|
188
187
|
type: :runtime
|
189
188
|
prerelease: false
|
190
189
|
version_requirements: !ruby/object:Gem::Requirement
|
191
190
|
requirements:
|
192
191
|
- - "~>"
|
193
192
|
- !ruby/object:Gem::Version
|
194
|
-
version: '2.
|
193
|
+
version: '2.7'
|
195
194
|
- !ruby/object:Gem::Dependency
|
196
195
|
name: acme-client
|
197
196
|
requirement: !ruby/object:Gem::Requirement
|
@@ -374,7 +373,6 @@ dependencies:
|
|
374
373
|
- - "~>"
|
375
374
|
- !ruby/object:Gem::Version
|
376
375
|
version: '0.9'
|
377
|
-
description:
|
378
376
|
email:
|
379
377
|
- jonathan.gnagy@gmail.com
|
380
378
|
executables: []
|
@@ -394,6 +392,7 @@ files:
|
|
394
392
|
- Dockerfile
|
395
393
|
- Gemfile
|
396
394
|
- Gemfile.lock
|
395
|
+
- Itsi.rb
|
397
396
|
- LICENSE.txt
|
398
397
|
- README.md
|
399
398
|
- Rakefile
|
@@ -401,7 +400,6 @@ files:
|
|
401
400
|
- bin/setup
|
402
401
|
- bullion.gemspec
|
403
402
|
- config.ru
|
404
|
-
- config/puma.rb
|
405
403
|
- db/migrate/20210104000000_create_accounts.rb
|
406
404
|
- db/migrate/20210104060422_create_certificates.rb
|
407
405
|
- db/migrate/20210105060406_create_orders.rb
|
@@ -424,6 +422,7 @@ files:
|
|
424
422
|
- lib/bullion/models/challenge.rb
|
425
423
|
- lib/bullion/models/nonce.rb
|
426
424
|
- lib/bullion/models/order.rb
|
425
|
+
- lib/bullion/models/order_csr.rb
|
427
426
|
- lib/bullion/rspec/challenge_clients/dns.rb
|
428
427
|
- lib/bullion/rspec/challenge_clients/http.rb
|
429
428
|
- lib/bullion/service.rb
|
@@ -442,7 +441,6 @@ licenses:
|
|
442
441
|
metadata:
|
443
442
|
homepage_uri: https://github.com/jgnagy/bullion
|
444
443
|
source_code_uri: https://github.com/jgnagy/bullion
|
445
|
-
post_install_message:
|
446
444
|
rdoc_options: []
|
447
445
|
require_paths:
|
448
446
|
- lib
|
@@ -450,15 +448,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
450
448
|
requirements:
|
451
449
|
- - "~>"
|
452
450
|
- !ruby/object:Gem::Version
|
453
|
-
version: '3.
|
451
|
+
version: '3.4'
|
454
452
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
455
453
|
requirements:
|
456
454
|
- - ">="
|
457
455
|
- !ruby/object:Gem::Version
|
458
456
|
version: '0'
|
459
457
|
requirements: []
|
460
|
-
rubygems_version: 3.
|
461
|
-
signing_key:
|
458
|
+
rubygems_version: 3.6.7
|
462
459
|
specification_version: 4
|
463
460
|
summary: Ruby ACME v2 Certificate Authority
|
464
461
|
test_files: []
|
data/config/puma.rb
DELETED