1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
//! **The `openssl` half of the mint** (REMOTE §1.4, §8; bl-ae05): the tool
//! invocations, and the two facts a certificate carries that are yog's to
//! decide — the subject alternative name and the extended key usage.
//!
//! Split from [`provision`](super) at the seam the 300-line cap and the §12
//! pre-split band both name: **which artifacts a box needs** is a question
//! about the box, and **what one `openssl` run says** is a question about
//! X.509. Nothing here reads the directory's state; nothing there spells a
//! flag.
//!
//! yog links no certificate library (AGENTS.md rule 6) — this shells to the
//! tool an operator would use, through the crate's one command constructor.
use ;
use crategit_env;
use crateGrade;
use crateRole;
use IpAddr;
use Path;
/// The extension file's suffix. Scratch, deleted with the request it rode
/// beside.
const EXT: &str = "ext";
/// The section `-extensions` names inside it. A name rather than the unnamed
/// default section, because "which section" is then stated rather than
/// inferred by two different tools' defaults.
const SECTION: &str = "leaf";
/// The serial counter `-CAcreateserial` derives from the anchor's name — the
/// `openssl` convention, which is why it is spelled here and not beside
/// [`ANCHORS`].
const SERIAL: &str = "ca.srl";
/// The self-signed operator CA both ends verify against.
pub
/// One of the three roles' leaves: [`Role`] derives the basename it is written
/// under, the common name it carries — which **is** the client identity the
/// engine reads back off the presented certificate (REMOTE §2) — and the two
/// X.509 facts below. `hosts` is every way in the server answers to; a client
/// leaf ignores it, because nothing dials a client.
pub
/// A client leaf under a **stated** common name (REMOTE §8.2, bl-64a7): the
/// host half of provisioning an entry, and [`Role::Client`]'s own recipe with
/// the operator's name where the role's would be — the client EKU, and a SAN
/// naming the leaf itself, because nothing dials a client. No host is named
/// anywhere in it, which is what lets the pair be carried to whichever box the
/// operator hands it to.
///
/// The pair is written under the common name itself (`<cn>.pem`/`<cn>.key`),
/// because a directory holding several must say which is which. That basename
/// is a filing convenience and nothing more: the name **inside** is the
/// identity (REMOTE §2), and on the client box the pair is placed into
/// `wire/workspaces/<workspace>/` as `client.pem`/`client.key` (§8.2) — that
/// directory named for the workspace it addresses, not for this common name —
/// without changing what it authenticates as.
///
/// **`grade` is the other thing the subject says** (REMOTE §4.2, bl-7ff3), and
/// it is written here because the operator's own CA is the only thing entitled
/// to write it. A foot's subject gains one organizational unit; an operator's
/// is the bare common name it always was, so every leaf minted before the grade
/// existed reads back exactly as it did.
pub
/// The subject a stated leaf is minted under. Most-general attribute first,
/// which is DER's own order and the reverse of how RFC 4514 renders it — so
/// `OU=foot` precedes the common name, and the walk that reads the name back
/// (which takes the LAST one) is unaffected either way.
/// The issuance itself: a key, a bare request, then the signature that carries
/// the SAN and EKU it was handed.
///
/// **The extensions are the issuer's, and they are handed over in a file**
/// (bl-8626). The obvious spelling — `req -addext` to put them in the request
/// and `x509 -copy_extensions copy` to carry them across — is OpenSSL-only:
/// macOS ships LibreSSL as `openssl`, whose `x509` has no `-copy_extensions`
/// and refuses the whole invocation (`Unrecognized flag copy_extensions`),
/// which is every wire test on that platform. `-extfile`/`-extensions` is the
/// spelling both toolsets have had for decades, and it is the more honest
/// model besides: what a certificate asserts is decided by whoever signs it,
/// not by whoever asked. One recipe, both toolsets — never a second recipe and
/// never a platform gate.
/// A leaf's subject alternative name. The **server**'s names every way in the
/// box answers to, because a seat verifies what it dialled against this list —
/// an IP literal is an IP identity and anything else a DNS one, the same rule
/// [`client`](super::client) reads it back by, applied to each entry alike. A
/// client leaf's names itself: nothing dials a client.
///
/// **A box is reachable more than one way, and saying so is not a rotation**
/// (bl-52f4). A host on an overlay network has a resolvable name, an overlay
/// address and a LAN address, and different clients reach it differently — a
/// device whose resolver is its emulator's cannot use the name, and an address
/// the certificate omits fails *verification* rather than routing, so the error
/// names trust where the fact is reachability. One host per certificate made
/// the remedy `FORCE=1`, which re-founds the CA and strands every client leaf
/// already carried away. The list is the dissolution: state every spelling
/// once.
///
/// **Loopback is always on the server leaf** (bl-ae05). The local window is a
/// client of `127.0.0.1` unconditionally — that is what the ruling means by the
/// front door — so a server certificate that only named an operator's public
/// host would refuse the one seat that is certain to be there. It costs one
/// SAN entry and removes a whole class of "the window cannot reach its own
/// engine". It is appended, and the whole list is de-duplicated as it is built,
/// so a box stated as loopback says it once.
pub
/// A leaf's extended key usage: the server end authenticates as a server, and
/// both client ends as clients.
pub
/// One `openssl` run. The tool is named once, here — [`run`] takes it as a
/// parameter only so a test can drive the two failure paths without
/// uninstalling anything.
pub
/// One run of `program`, through the crate's one command constructor
/// (`rules/no-bare-command.yml`). Its stderr is the refusal's text, trimmed:
/// an operator whose mint failed needs the tool's own sentence.
pub