@marcel91200 / caddy Mirror · read-only
Fork 0

Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS

Mirrored from https://github.com/Slazee/caddy.git

Synced Nov 11, 01:00 UTC

master
go 293 lines 7.43 KB
Raw
1 // Copyright 2015 Matthew Holt and The Caddy Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 package httpcaddyfile
16
17 import (
18 "slices"
19 "strconv"
20
21 "github.com/caddyserver/caddy/v2"
22 "github.com/caddyserver/caddy/v2/caddyconfig"
23 "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
24 "github.com/caddyserver/caddy/v2/modules/caddypki"
25 "github.com/caddyserver/caddy/v2/modules/caddytls"
26 )
27
28 func init() {
29 RegisterGlobalOption("pki", parsePKIApp)
30 }
31
32 // parsePKIApp parses the global pki option. Syntax:
33 //
34 // pki {
35 // ca [<id>] {
36 // name <name>
37 // root_cn <name>
38 // intermediate_cn <name>
39 // intermediate_lifetime <duration>
40 // maintenance_interval <duration>
41 // renewal_window_ratio <ratio>
42 // root {
43 // cert <path>
44 // key <path>
45 // format <format>
46 // }
47 // intermediate {
48 // cert <path>
49 // key <path>
50 // format <format>
51 // }
52 // }
53 // }
54 //
55 // When the CA ID is unspecified, 'local' is assumed.
56 func parsePKIApp(d *caddyfile.Dispenser, existingVal any) (any, error) {
57 d.Next() // consume app name
58
59 pki := &caddypki.PKI{
60 CAs: make(map[string]*caddypki.CA),
61 }
62 for d.NextBlock(0) {
63 switch d.Val() {
64 case "ca":
65 pkiCa := new(caddypki.CA)
66 if d.NextArg() {
67 pkiCa.ID = d.Val()
68 if d.NextArg() {
69 return nil, d.ArgErr()
70 }
71 }
72 if pkiCa.ID == "" {
73 pkiCa.ID = caddypki.DefaultCAID
74 }
75
76 for nesting := d.Nesting(); d.NextBlock(nesting); {
77 switch d.Val() {
78 case "name":
79 if !d.NextArg() {
80 return nil, d.ArgErr()
81 }
82 pkiCa.Name = d.Val()
83
84 case "root_cn":
85 if !d.NextArg() {
86 return nil, d.ArgErr()
87 }
88 pkiCa.RootCommonName = d.Val()
89
90 case "intermediate_cn":
91 if !d.NextArg() {
92 return nil, d.ArgErr()
93 }
94 pkiCa.IntermediateCommonName = d.Val()
95
96 case "intermediate_lifetime":
97 if !d.NextArg() {
98 return nil, d.ArgErr()
99 }
100 dur, err := caddy.ParseDuration(d.Val())
101 if err != nil {
102 return nil, err
103 }
104 pkiCa.IntermediateLifetime = caddy.Duration(dur)
105
106 case "maintenance_interval":
107 if !d.NextArg() {
108 return nil, d.ArgErr()
109 }
110 dur, err := caddy.ParseDuration(d.Val())
111 if err != nil {
112 return nil, err
113 }
114 pkiCa.MaintenanceInterval = caddy.Duration(dur)
115
116 case "renewal_window_ratio":
117 if !d.NextArg() {
118 return nil, d.ArgErr()
119 }
120 ratio, err := strconv.ParseFloat(d.Val(), 64)
121 if err != nil || ratio <= 0 || ratio > 1 {
122 return nil, d.Errf("renewal_window_ratio must be a number in (0, 1], got %s", d.Val())
123 }
124 pkiCa.RenewalWindowRatio = ratio
125
126 case "root":
127 if pkiCa.Root == nil {
128 pkiCa.Root = new(caddypki.KeyPair)
129 }
130 for nesting := d.Nesting(); d.NextBlock(nesting); {
131 switch d.Val() {
132 case "cert":
133 if !d.NextArg() {
134 return nil, d.ArgErr()
135 }
136 pkiCa.Root.Certificate = d.Val()
137
138 case "key":
139 if !d.NextArg() {
140 return nil, d.ArgErr()
141 }
142 pkiCa.Root.PrivateKey = d.Val()
143
144 case "format":
145 if !d.NextArg() {
146 return nil, d.ArgErr()
147 }
148 pkiCa.Root.Format = d.Val()
149
150 default:
151 return nil, d.Errf("unrecognized pki ca root option '%s'", d.Val())
152 }
153 }
154
155 case "intermediate":
156 if pkiCa.Intermediate == nil {
157 pkiCa.Intermediate = new(caddypki.KeyPair)
158 }
159 for nesting := d.Nesting(); d.NextBlock(nesting); {
160 switch d.Val() {
161 case "cert":
162 if !d.NextArg() {
163 return nil, d.ArgErr()
164 }
165 pkiCa.Intermediate.Certificate = d.Val()
166
167 case "key":
168 if !d.NextArg() {
169 return nil, d.ArgErr()
170 }
171 pkiCa.Intermediate.PrivateKey = d.Val()
172
173 case "format":
174 if !d.NextArg() {
175 return nil, d.ArgErr()
176 }
177 pkiCa.Intermediate.Format = d.Val()
178
179 default:
180 return nil, d.Errf("unrecognized pki ca intermediate option '%s'", d.Val())
181 }
182 }
183
184 default:
185 return nil, d.Errf("unrecognized pki ca option '%s'", d.Val())
186 }
187 }
188
189 pki.CAs[pkiCa.ID] = pkiCa
190
191 default:
192 return nil, d.Errf("unrecognized pki option '%s'", d.Val())
193 }
194 }
195 return pki, nil
196 }
197
198 func (st ServerType) buildPKIApp(
199 pairings []sbAddrAssociation,
200 options map[string]any,
201 warnings []caddyconfig.Warning,
202 ) (*caddypki.PKI, []caddyconfig.Warning, error) {
203 skipInstallTrust := false
204 if _, ok := options["skip_install_trust"]; ok {
205 skipInstallTrust = true
206 }
207
208 // check if auto_https is off - in that case we should not create
209 // any PKI infrastructure even with skip_install_trust directive
210 autoHTTPS := []string{}
211 if ah, ok := options["auto_https"].([]string); ok {
212 autoHTTPS = ah
213 }
214 autoHTTPSOff := slices.Contains(autoHTTPS, "off")
215
216 falseBool := false
217
218 // Load the PKI app configured via global options
219 var pkiApp *caddypki.PKI
220 unwrappedPki, ok := options["pki"].(*caddypki.PKI)
221 if ok {
222 pkiApp = unwrappedPki
223 } else {
224 pkiApp = &caddypki.PKI{CAs: make(map[string]*caddypki.CA)}
225 }
226 for _, ca := range pkiApp.CAs {
227 if skipInstallTrust {
228 ca.InstallTrust = &falseBool
229 }
230 pkiApp.CAs[ca.ID] = ca
231 }
232
233 // Add in the CAs configured via directives
234 for _, p := range pairings {
235 for _, sblock := range p.serverBlocks {
236 // find all the CAs that were defined and add them to the app config
237 // i.e. from any "acme_server" directives
238 for _, caCfgValue := range sblock.pile["pki.ca"] {
239 ca := caCfgValue.Value.(*caddypki.CA)
240 if skipInstallTrust {
241 ca.InstallTrust = &falseBool
242 }
243
244 // the CA might already exist from global options, so
245 // don't overwrite it in that case
246 if _, ok := pkiApp.CAs[ca.ID]; !ok {
247 pkiApp.CAs[ca.ID] = ca
248 }
249 }
250 }
251 }
252
253 // if there was no CAs defined in any of the servers,
254 // and we were requested to not install trust, then
255 // add one for the default/local CA to do so
256 // only if auto_https is not completely disabled
257 if len(pkiApp.CAs) == 0 && skipInstallTrust && !autoHTTPSOff {
258 ca := new(caddypki.CA)
259 ca.ID = caddypki.DefaultCAID
260 ca.InstallTrust = &falseBool
261 pkiApp.CAs[ca.ID] = ca
262 }
263
264 // an explicit site-level internal issuer creates its own CA
265 // regardless of `auto_https`, so make sure `skip_install_trust`
266 // is honored for it too; otherwise the CA is created later at
267 // runtime with trust installation left on
268 if skipInstallTrust {
269 for _, p := range pairings {
270 for _, sblock := range p.serverBlocks {
271 for _, issuerCfgValue := range sblock.pile["tls.cert_issuer"] {
272 internalIssuer, ok := issuerCfgValue.Value.(*caddytls.InternalIssuer)
273 if !ok {
274 continue
275 }
276 caID := internalIssuer.CA
277 if caID == "" {
278 caID = caddypki.DefaultCAID
279 }
280 if _, ok := pkiApp.CAs[caID]; ok {
281 continue
282 }
283 ca := new(caddypki.CA)
284 ca.ID = caID
285 ca.InstallTrust = &falseBool
286 pkiApp.CAs[ca.ID] = ca
287 }
288 }
289 }
290 }
291
292 return pkiApp, warnings, nil
293 }