@marcel91200 / caddy Mirror · read-only / commits / b3d35a49
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 14, 05:15 UTC

httpcaddyfile: Don't put localhost in public APs (fix #4220)

If an email is specified in global options, a site called 'localhost' shouldn't be bunched together with public DNS names in the automation policies, which get the default, public-CA issuers. Fix old test that did this. I also noticed that these two: localhost { } example.com { } and localhost, example.com { } produce slightly different TLS automation policies. The former is what the new test case covers, and we have logic that removes the empty automation policy for localhost so that auto-HTTPS can implicitly create one. (We prefer that whenever possible.) But the latter case produces two automation policies, with the second one being for localhost, with an explicit internal issuer. It's not wrong, just more explicit than it needs to be. I'd really like to completely rewrite the code from scratch that generates automation policies, hopefully there is a simpler, more correct algorithm.

Matthew Holt committed Jun 25, 2021 at 11:28 UTC b3d35a4995c98cfadeb0c3a356025dbd3984caef
3 files changed +102 -7
caddyconfig/httpcaddyfile/tlsapp.go
+33 -4
@@ -211,7 +211,7 @@ func (st ServerType) buildTLSApp(
211 // it that we would need to check here) since the hostname is known at handshake;
212 // and it is unexpected to switch to internal issuer when the user wants to get
213 // regular certificates on-demand for a class of certs like *.*.tld.
214 - if !certmagic.SubjectIsIP(s) && !certmagic.SubjectIsInternal(s) && (strings.Count(s, "*.") < 2 || ap.OnDemand) {
214 + if subjectQualifiesForPublicCert(ap, s) {
215 external = append(external, s)
216 } else {
217 internal = append(internal, s)
@@ -324,8 +324,12 @@ func (st ServerType) buildTLSApp(
324 globalPreferredChains := options["preferred_chains"]
325 hasGlobalACMEDefaults := globalEmail != nil || globalACMECA != nil || globalACMECARoot != nil || globalACMEDNS != nil || globalACMEEAB != nil || globalPreferredChains != nil
326 if hasGlobalACMEDefaults {
327 - for _, ap := range tlsApp.Automation.Policies {
328 - if len(ap.Issuers) == 0 {
327 + // for _, ap := range tlsApp.Automation.Policies {
328 + for i := 0; i < len(tlsApp.Automation.Policies); i++ {
329 + ap := tlsApp.Automation.Policies[i]
330 + if len(ap.Issuers) == 0 && automationPolicyHasAllPublicNames(ap) {
331 + // for public names, create default issuers which will later be filled in with configured global defaults
332 + // (internal names will implicitly use the internal issuer at auto-https time)
333 ap.Issuers = caddytls.DefaultIssuers()
334
335 // if a specific endpoint is configured, can't use multiple default issuers
@@ -494,16 +498,23 @@ func consolidateAutomationPolicies(aps []*caddytls.AutomationPolicy) []*caddytls
498 })
499
500 emptyAPCount := 0
501 + origLenAPs := len(aps)
502 // compute the number of empty policies (disregarding subjects) - see #4128
503 emptyAP := new(caddytls.AutomationPolicy)
504 for i := 0; i < len(aps); i++ {
505 emptyAP.Subjects = aps[i].Subjects
506 if reflect.DeepEqual(aps[i], emptyAP) {
507 emptyAPCount++
508 + if !automationPolicyHasAllPublicNames(aps[i]) {
509 + // if this automation policy has internal names, we might as well remove it
510 + // so auto-https can implicitly use the internal issuer
511 + aps = append(aps[:i], aps[i+1:]...)
512 + i--
513 + }
514 }
515 }
516 // If all policies are empty, we can return nil, as there is no need to set any policy
506 - if emptyAPCount == len(aps) {
517 + if emptyAPCount == origLenAPs {
518 return nil
519 }
520
@@ -601,3 +612,21 @@ func automationPolicyShadows(i int, aps []*caddytls.AutomationPolicy) int {
612 }
613 return -1
614 }
615 +
616 +// subjectQualifiesForPublicCert is like certmagic.SubjectQualifiesForPublicCert() except
617 +// that this allows domains with multiple wildcard levels like '*.*.example.com' to qualify
618 +// if the automation policy has OnDemand enabled (i.e. this function is more lenient).
619 +func subjectQualifiesForPublicCert(ap *caddytls.AutomationPolicy, subj string) bool {
620 + return !certmagic.SubjectIsIP(subj) &&
621 + !certmagic.SubjectIsInternal(subj) &&
622 + (strings.Count(subj, "*.") < 2 || ap.OnDemand)
623 +}
624 +
625 +func automationPolicyHasAllPublicNames(ap *caddytls.AutomationPolicy) bool {
626 + for _, subj := range ap.Subjects {
627 + if !subjectQualifiesForPublicCert(ap, subj) {
628 + return false
629 + }
630 + }
631 + return true
632 +}
caddytest/integration/caddyfile_adapt/global_options_preferred_chains.txt
+3 -3
@@ -2,7 +2,7 @@
2 preferred_chains smallest
3 }
4
5 -localhost
5 +example.com
6 ----------
7 {
8 "apps": {
@@ -17,7 +17,7 @@ localhost
17 "match": [
18 {
19 "host": [
20 - "localhost"
20 + "example.com"
21 ]
22 }
23 ],
@@ -32,7 +32,7 @@ localhost
32 "policies": [
33 {
34 "subjects": [
35 - "localhost"
35 + "example.com"
36 ],
37 "issuers": [
38 {
caddytest/integration/caddyfile_adapt/tls_automation_policies_global_email_localhost.txt new
+66
@@ -0,0 +1,66 @@
1 +{
2 + email foo@bar
3 +}
4 +
5 +localhost {
6 +}
7 +
8 +example.com {
9 +}
10 +----------
11 +{
12 + "apps": {
13 + "http": {
14 + "servers": {
15 + "srv0": {
16 + "listen": [
17 + ":443"
18 + ],
19 + "routes": [
20 + {
21 + "match": [
22 + {
23 + "host": [
24 + "example.com"
25 + ]
26 + }
27 + ],
28 + "terminal": true
29 + },
30 + {
31 + "match": [
32 + {
33 + "host": [
34 + "localhost"
35 + ]
36 + }
37 + ],
38 + "terminal": true
39 + }
40 + ]
41 + }
42 + }
43 + },
44 + "tls": {
45 + "automation": {
46 + "policies": [
47 + {
48 + "subjects": [
49 + "example.com"
50 + ],
51 + "issuers": [
52 + {
53 + "email": "foo@bar",
54 + "module": "acme"
55 + },
56 + {
57 + "email": "foo@bar",
58 + "module": "zerossl"
59 + }
60 + ]
61 + }
62 + ]
63 + }
64 + }
65 + }
66 +}
\ No newline at end of file