mirror of
https://github.com/caddyserver/caddy.git
synced 2024-12-16 21:56:40 -05:00
caddyhttp: Implement auto_https prefer_wildcard
option (#6146)
* Allow specifying multiple `auto_https` options * Implement `auto_https prefer_wildcard` option * Adapt tests, add mock DNS module for config testing * Rebase fix
This commit is contained in:
parent
792f1c7ed7
commit
16724842d9
7 changed files with 449 additions and 20 deletions
|
@ -534,8 +534,8 @@ func (st *ServerType) serversFromPairings(
|
||||||
if hsp, ok := options["https_port"].(int); ok {
|
if hsp, ok := options["https_port"].(int); ok {
|
||||||
httpsPort = strconv.Itoa(hsp)
|
httpsPort = strconv.Itoa(hsp)
|
||||||
}
|
}
|
||||||
autoHTTPS := "on"
|
autoHTTPS := []string{}
|
||||||
if ah, ok := options["auto_https"].(string); ok {
|
if ah, ok := options["auto_https"].([]string); ok {
|
||||||
autoHTTPS = ah
|
autoHTTPS = ah
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -594,17 +594,37 @@ func (st *ServerType) serversFromPairings(
|
||||||
}
|
}
|
||||||
|
|
||||||
// handle the auto_https global option
|
// handle the auto_https global option
|
||||||
if autoHTTPS != "on" {
|
for _, val := range autoHTTPS {
|
||||||
srv.AutoHTTPS = new(caddyhttp.AutoHTTPSConfig)
|
switch val {
|
||||||
switch autoHTTPS {
|
|
||||||
case "off":
|
case "off":
|
||||||
|
if srv.AutoHTTPS == nil {
|
||||||
|
srv.AutoHTTPS = new(caddyhttp.AutoHTTPSConfig)
|
||||||
|
}
|
||||||
srv.AutoHTTPS.Disabled = true
|
srv.AutoHTTPS.Disabled = true
|
||||||
|
|
||||||
case "disable_redirects":
|
case "disable_redirects":
|
||||||
|
if srv.AutoHTTPS == nil {
|
||||||
|
srv.AutoHTTPS = new(caddyhttp.AutoHTTPSConfig)
|
||||||
|
}
|
||||||
srv.AutoHTTPS.DisableRedir = true
|
srv.AutoHTTPS.DisableRedir = true
|
||||||
|
|
||||||
case "disable_certs":
|
case "disable_certs":
|
||||||
|
if srv.AutoHTTPS == nil {
|
||||||
|
srv.AutoHTTPS = new(caddyhttp.AutoHTTPSConfig)
|
||||||
|
}
|
||||||
srv.AutoHTTPS.DisableCerts = true
|
srv.AutoHTTPS.DisableCerts = true
|
||||||
|
|
||||||
case "ignore_loaded_certs":
|
case "ignore_loaded_certs":
|
||||||
|
if srv.AutoHTTPS == nil {
|
||||||
|
srv.AutoHTTPS = new(caddyhttp.AutoHTTPSConfig)
|
||||||
|
}
|
||||||
srv.AutoHTTPS.IgnoreLoadedCerts = true
|
srv.AutoHTTPS.IgnoreLoadedCerts = true
|
||||||
|
|
||||||
|
case "prefer_wildcard":
|
||||||
|
if srv.AutoHTTPS == nil {
|
||||||
|
srv.AutoHTTPS = new(caddyhttp.AutoHTTPSConfig)
|
||||||
|
}
|
||||||
|
srv.AutoHTTPS.PreferWildcard = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -673,7 +693,7 @@ func (st *ServerType) serversFromPairings(
|
||||||
})
|
})
|
||||||
|
|
||||||
var hasCatchAllTLSConnPolicy, addressQualifiesForTLS bool
|
var hasCatchAllTLSConnPolicy, addressQualifiesForTLS bool
|
||||||
autoHTTPSWillAddConnPolicy := autoHTTPS != "off"
|
autoHTTPSWillAddConnPolicy := srv.AutoHTTPS == nil || !srv.AutoHTTPS.Disabled
|
||||||
|
|
||||||
// if needed, the ServerLogConfig is initialized beforehand so
|
// if needed, the ServerLogConfig is initialized beforehand so
|
||||||
// that all server blocks can populate it with data, even when not
|
// that all server blocks can populate it with data, even when not
|
||||||
|
@ -757,6 +777,13 @@ func (st *ServerType) serversFromPairings(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wildcardHosts := []string{}
|
||||||
|
for _, addr := range sblock.parsedKeys {
|
||||||
|
if strings.HasPrefix(addr.Host, "*.") {
|
||||||
|
wildcardHosts = append(wildcardHosts, addr.Host[2:])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for _, addr := range sblock.parsedKeys {
|
for _, addr := range sblock.parsedKeys {
|
||||||
// if server only uses HTTP port, auto-HTTPS will not apply
|
// if server only uses HTTP port, auto-HTTPS will not apply
|
||||||
if listenersUseAnyPortOtherThan(srv.Listen, httpPort) {
|
if listenersUseAnyPortOtherThan(srv.Listen, httpPort) {
|
||||||
|
@ -772,6 +799,18 @@ func (st *ServerType) serversFromPairings(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If prefer wildcard is enabled, then we add hosts that are
|
||||||
|
// already covered by the wildcard to the skip list
|
||||||
|
if srv.AutoHTTPS != nil && srv.AutoHTTPS.PreferWildcard && addr.Scheme == "https" {
|
||||||
|
baseDomain := addr.Host
|
||||||
|
if idx := strings.Index(baseDomain, "."); idx != -1 {
|
||||||
|
baseDomain = baseDomain[idx+1:]
|
||||||
|
}
|
||||||
|
if !strings.HasPrefix(addr.Host, "*.") && slices.Contains(wildcardHosts, baseDomain) {
|
||||||
|
srv.AutoHTTPS.Skip = append(srv.AutoHTTPS.Skip, addr.Host)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// If TLS is specified as directive, it will also result in 1 or more connection policy being created
|
// If TLS is specified as directive, it will also result in 1 or more connection policy being created
|
||||||
// Thus, catch-all address with non-standard port, e.g. :8443, can have TLS enabled without
|
// Thus, catch-all address with non-standard port, e.g. :8443, can have TLS enabled without
|
||||||
// specifying prefix "https://"
|
// specifying prefix "https://"
|
||||||
|
@ -919,7 +958,10 @@ func (st *ServerType) serversFromPairings(
|
||||||
if addressQualifiesForTLS &&
|
if addressQualifiesForTLS &&
|
||||||
!hasCatchAllTLSConnPolicy &&
|
!hasCatchAllTLSConnPolicy &&
|
||||||
(len(srv.TLSConnPolicies) > 0 || !autoHTTPSWillAddConnPolicy || defaultSNI != "" || fallbackSNI != "") {
|
(len(srv.TLSConnPolicies) > 0 || !autoHTTPSWillAddConnPolicy || defaultSNI != "" || fallbackSNI != "") {
|
||||||
srv.TLSConnPolicies = append(srv.TLSConnPolicies, &caddytls.ConnectionPolicy{DefaultSNI: defaultSNI, FallbackSNI: fallbackSNI})
|
srv.TLSConnPolicies = append(srv.TLSConnPolicies, &caddytls.ConnectionPolicy{
|
||||||
|
DefaultSNI: defaultSNI,
|
||||||
|
FallbackSNI: fallbackSNI,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// tidy things up a bit
|
// tidy things up a bit
|
||||||
|
|
|
@ -452,15 +452,22 @@ func parseOptPersistConfig(d *caddyfile.Dispenser, _ any) (any, error) {
|
||||||
|
|
||||||
func parseOptAutoHTTPS(d *caddyfile.Dispenser, _ any) (any, error) {
|
func parseOptAutoHTTPS(d *caddyfile.Dispenser, _ any) (any, error) {
|
||||||
d.Next() // consume option name
|
d.Next() // consume option name
|
||||||
if !d.Next() {
|
val := d.RemainingArgs()
|
||||||
|
if len(val) == 0 {
|
||||||
return "", d.ArgErr()
|
return "", d.ArgErr()
|
||||||
}
|
}
|
||||||
val := d.Val()
|
for _, v := range val {
|
||||||
if d.Next() {
|
switch v {
|
||||||
return "", d.ArgErr()
|
case "off":
|
||||||
}
|
case "disable_redirects":
|
||||||
if val != "off" && val != "disable_redirects" && val != "disable_certs" && val != "ignore_loaded_certs" {
|
case "disable_certs":
|
||||||
return "", d.Errf("auto_https must be one of 'off', 'disable_redirects', 'disable_certs', or 'ignore_loaded_certs'")
|
case "ignore_loaded_certs":
|
||||||
|
case "prefer_wildcard":
|
||||||
|
break
|
||||||
|
|
||||||
|
default:
|
||||||
|
return "", d.Errf("auto_https must be one of 'off', 'disable_redirects', 'disable_certs', 'ignore_loaded_certs', or 'prefer_wildcard'")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return val, nil
|
return val, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,8 +45,8 @@ func (st ServerType) buildTLSApp(
|
||||||
if hp, ok := options["http_port"].(int); ok {
|
if hp, ok := options["http_port"].(int); ok {
|
||||||
httpPort = strconv.Itoa(hp)
|
httpPort = strconv.Itoa(hp)
|
||||||
}
|
}
|
||||||
autoHTTPS := "on"
|
autoHTTPS := []string{}
|
||||||
if ah, ok := options["auto_https"].(string); ok {
|
if ah, ok := options["auto_https"].([]string); ok {
|
||||||
autoHTTPS = ah
|
autoHTTPS = ah
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,13 +54,14 @@ func (st ServerType) buildTLSApp(
|
||||||
// key, so that they don't get forgotten/omitted by auto-HTTPS
|
// key, so that they don't get forgotten/omitted by auto-HTTPS
|
||||||
// (since they won't appear in route matchers)
|
// (since they won't appear in route matchers)
|
||||||
httpsHostsSharedWithHostlessKey := make(map[string]struct{})
|
httpsHostsSharedWithHostlessKey := make(map[string]struct{})
|
||||||
if autoHTTPS != "off" {
|
if !slices.Contains(autoHTTPS, "off") {
|
||||||
for _, pair := range pairings {
|
for _, pair := range pairings {
|
||||||
for _, sb := range pair.serverBlocks {
|
for _, sb := range pair.serverBlocks {
|
||||||
for _, addr := range sb.parsedKeys {
|
for _, addr := range sb.parsedKeys {
|
||||||
if addr.Host != "" {
|
if addr.Host != "" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// this server block has a hostless key, now
|
// this server block has a hostless key, now
|
||||||
// go through and add all the hosts to the set
|
// go through and add all the hosts to the set
|
||||||
for _, otherAddr := range sb.parsedKeys {
|
for _, otherAddr := range sb.parsedKeys {
|
||||||
|
@ -350,7 +351,7 @@ func (st ServerType) buildTLSApp(
|
||||||
internalAP := &caddytls.AutomationPolicy{
|
internalAP := &caddytls.AutomationPolicy{
|
||||||
IssuersRaw: []json.RawMessage{json.RawMessage(`{"module":"internal"}`)},
|
IssuersRaw: []json.RawMessage{json.RawMessage(`{"module":"internal"}`)},
|
||||||
}
|
}
|
||||||
if autoHTTPS != "off" && autoHTTPS != "disable_certs" {
|
if !slices.Contains(autoHTTPS, "off") && !slices.Contains(autoHTTPS, "disable_certs") {
|
||||||
for h := range httpsHostsSharedWithHostlessKey {
|
for h := range httpsHostsSharedWithHostlessKey {
|
||||||
al = append(al, h)
|
al = append(al, h)
|
||||||
if !certmagic.SubjectQualifiesForPublicCert(h) {
|
if !certmagic.SubjectQualifiesForPublicCert(h) {
|
||||||
|
@ -417,7 +418,10 @@ func (st ServerType) buildTLSApp(
|
||||||
}
|
}
|
||||||
|
|
||||||
// consolidate automation policies that are the exact same
|
// consolidate automation policies that are the exact same
|
||||||
tlsApp.Automation.Policies = consolidateAutomationPolicies(tlsApp.Automation.Policies)
|
tlsApp.Automation.Policies = consolidateAutomationPolicies(
|
||||||
|
tlsApp.Automation.Policies,
|
||||||
|
slices.Contains(autoHTTPS, "prefer_wildcard"),
|
||||||
|
)
|
||||||
|
|
||||||
// ensure automation policies don't overlap subjects (this should be
|
// ensure automation policies don't overlap subjects (this should be
|
||||||
// an error at provision-time as well, but catch it in the adapt phase
|
// an error at provision-time as well, but catch it in the adapt phase
|
||||||
|
@ -563,7 +567,7 @@ func newBaseAutomationPolicy(
|
||||||
|
|
||||||
// consolidateAutomationPolicies combines automation policies that are the same,
|
// consolidateAutomationPolicies combines automation policies that are the same,
|
||||||
// for a cleaner overall output.
|
// for a cleaner overall output.
|
||||||
func consolidateAutomationPolicies(aps []*caddytls.AutomationPolicy) []*caddytls.AutomationPolicy {
|
func consolidateAutomationPolicies(aps []*caddytls.AutomationPolicy, preferWildcard bool) []*caddytls.AutomationPolicy {
|
||||||
// sort from most specific to least specific; we depend on this ordering
|
// sort from most specific to least specific; we depend on this ordering
|
||||||
sort.SliceStable(aps, func(i, j int) bool {
|
sort.SliceStable(aps, func(i, j int) bool {
|
||||||
if automationPolicyIsSubset(aps[i], aps[j]) {
|
if automationPolicyIsSubset(aps[i], aps[j]) {
|
||||||
|
@ -648,6 +652,31 @@ outer:
|
||||||
j--
|
j--
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if preferWildcard {
|
||||||
|
// remove subjects from i if they're covered by a wildcard in j
|
||||||
|
iSubjs := aps[i].SubjectsRaw
|
||||||
|
for iSubj := 0; iSubj < len(iSubjs); iSubj++ {
|
||||||
|
for jSubj := range aps[j].SubjectsRaw {
|
||||||
|
if !strings.HasPrefix(aps[j].SubjectsRaw[jSubj], "*.") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if certmagic.MatchWildcard(aps[i].SubjectsRaw[iSubj], aps[j].SubjectsRaw[jSubj]) {
|
||||||
|
iSubjs = slices.Delete(iSubjs, iSubj, iSubj+1)
|
||||||
|
iSubj--
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
aps[i].SubjectsRaw = iSubjs
|
||||||
|
|
||||||
|
// remove i if it has no subjects left
|
||||||
|
if len(aps[i].SubjectsRaw) == 0 {
|
||||||
|
aps = slices.Delete(aps, i, i+1)
|
||||||
|
i--
|
||||||
|
continue outer
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,106 @@
|
||||||
|
{
|
||||||
|
auto_https prefer_wildcard
|
||||||
|
}
|
||||||
|
|
||||||
|
*.example.com {
|
||||||
|
tls {
|
||||||
|
dns mock
|
||||||
|
}
|
||||||
|
respond "fallback"
|
||||||
|
}
|
||||||
|
|
||||||
|
foo.example.com {
|
||||||
|
respond "foo"
|
||||||
|
}
|
||||||
|
----------
|
||||||
|
{
|
||||||
|
"apps": {
|
||||||
|
"http": {
|
||||||
|
"servers": {
|
||||||
|
"srv0": {
|
||||||
|
"listen": [
|
||||||
|
":443"
|
||||||
|
],
|
||||||
|
"routes": [
|
||||||
|
{
|
||||||
|
"match": [
|
||||||
|
{
|
||||||
|
"host": [
|
||||||
|
"foo.example.com"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"handle": [
|
||||||
|
{
|
||||||
|
"handler": "subroute",
|
||||||
|
"routes": [
|
||||||
|
{
|
||||||
|
"handle": [
|
||||||
|
{
|
||||||
|
"body": "foo",
|
||||||
|
"handler": "static_response"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"terminal": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"match": [
|
||||||
|
{
|
||||||
|
"host": [
|
||||||
|
"*.example.com"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"handle": [
|
||||||
|
{
|
||||||
|
"handler": "subroute",
|
||||||
|
"routes": [
|
||||||
|
{
|
||||||
|
"handle": [
|
||||||
|
{
|
||||||
|
"body": "fallback",
|
||||||
|
"handler": "static_response"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"terminal": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"automatic_https": {
|
||||||
|
"prefer_wildcard": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"tls": {
|
||||||
|
"automation": {
|
||||||
|
"policies": [
|
||||||
|
{
|
||||||
|
"subjects": [
|
||||||
|
"*.example.com"
|
||||||
|
],
|
||||||
|
"issuers": [
|
||||||
|
{
|
||||||
|
"challenges": {
|
||||||
|
"dns": {
|
||||||
|
"provider": {
|
||||||
|
"name": "mock"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"module": "acme"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,157 @@
|
||||||
|
*.example.com {
|
||||||
|
tls foo@example.com {
|
||||||
|
dns mock
|
||||||
|
}
|
||||||
|
|
||||||
|
@foo host foo.example.com
|
||||||
|
handle @foo {
|
||||||
|
respond "Foo!"
|
||||||
|
}
|
||||||
|
|
||||||
|
@bar host bar.example.com
|
||||||
|
handle @bar {
|
||||||
|
respond "Bar!"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Fallback for otherwise unhandled domains
|
||||||
|
handle {
|
||||||
|
abort
|
||||||
|
}
|
||||||
|
}
|
||||||
|
----------
|
||||||
|
{
|
||||||
|
"apps": {
|
||||||
|
"http": {
|
||||||
|
"servers": {
|
||||||
|
"srv0": {
|
||||||
|
"listen": [
|
||||||
|
":443"
|
||||||
|
],
|
||||||
|
"routes": [
|
||||||
|
{
|
||||||
|
"match": [
|
||||||
|
{
|
||||||
|
"host": [
|
||||||
|
"*.example.com"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"handle": [
|
||||||
|
{
|
||||||
|
"handler": "subroute",
|
||||||
|
"routes": [
|
||||||
|
{
|
||||||
|
"group": "group3",
|
||||||
|
"handle": [
|
||||||
|
{
|
||||||
|
"handler": "subroute",
|
||||||
|
"routes": [
|
||||||
|
{
|
||||||
|
"handle": [
|
||||||
|
{
|
||||||
|
"body": "Foo!",
|
||||||
|
"handler": "static_response"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"match": [
|
||||||
|
{
|
||||||
|
"host": [
|
||||||
|
"foo.example.com"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group": "group3",
|
||||||
|
"handle": [
|
||||||
|
{
|
||||||
|
"handler": "subroute",
|
||||||
|
"routes": [
|
||||||
|
{
|
||||||
|
"handle": [
|
||||||
|
{
|
||||||
|
"body": "Bar!",
|
||||||
|
"handler": "static_response"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"match": [
|
||||||
|
{
|
||||||
|
"host": [
|
||||||
|
"bar.example.com"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group": "group3",
|
||||||
|
"handle": [
|
||||||
|
{
|
||||||
|
"handler": "subroute",
|
||||||
|
"routes": [
|
||||||
|
{
|
||||||
|
"handle": [
|
||||||
|
{
|
||||||
|
"abort": true,
|
||||||
|
"handler": "static_response"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"terminal": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"tls": {
|
||||||
|
"automation": {
|
||||||
|
"policies": [
|
||||||
|
{
|
||||||
|
"subjects": [
|
||||||
|
"*.example.com"
|
||||||
|
],
|
||||||
|
"issuers": [
|
||||||
|
{
|
||||||
|
"challenges": {
|
||||||
|
"dns": {
|
||||||
|
"provider": {
|
||||||
|
"name": "mock"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"email": "foo@example.com",
|
||||||
|
"module": "acme"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ca": "https://acme.zerossl.com/v2/DV90",
|
||||||
|
"challenges": {
|
||||||
|
"dns": {
|
||||||
|
"provider": {
|
||||||
|
"name": "mock"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"email": "foo@example.com",
|
||||||
|
"module": "acme"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
61
caddytest/integration/mockdns_test.go
Normal file
61
caddytest/integration/mockdns_test.go
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
package integration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/caddyserver/caddy/v2"
|
||||||
|
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
|
||||||
|
"github.com/caddyserver/certmagic"
|
||||||
|
"github.com/libdns/libdns"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
caddy.RegisterModule(MockDNSProvider{})
|
||||||
|
}
|
||||||
|
|
||||||
|
// MockDNSProvider is a mock DNS provider, for testing config with DNS modules.
|
||||||
|
type MockDNSProvider struct{}
|
||||||
|
|
||||||
|
// CaddyModule returns the Caddy module information.
|
||||||
|
func (MockDNSProvider) CaddyModule() caddy.ModuleInfo {
|
||||||
|
return caddy.ModuleInfo{
|
||||||
|
ID: "dns.providers.mock",
|
||||||
|
New: func() caddy.Module { return new(MockDNSProvider) },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Provision sets up the module.
|
||||||
|
func (MockDNSProvider) Provision(ctx caddy.Context) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalCaddyfile sets up the module from Caddyfile tokens.
|
||||||
|
func (MockDNSProvider) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AppendsRecords appends DNS records to the zone.
|
||||||
|
func (MockDNSProvider) AppendRecords(ctx context.Context, zone string, recs []libdns.Record) ([]libdns.Record, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteRecords deletes DNS records from the zone.
|
||||||
|
func (MockDNSProvider) DeleteRecords(ctx context.Context, zone string, recs []libdns.Record) ([]libdns.Record, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRecords gets DNS records from the zone.
|
||||||
|
func (MockDNSProvider) GetRecords(ctx context.Context, zone string) ([]libdns.Record, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetRecords sets DNS records in the zone.
|
||||||
|
func (MockDNSProvider) SetRecords(ctx context.Context, zone string, recs []libdns.Record) ([]libdns.Record, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Interface guard
|
||||||
|
var _ caddyfile.Unmarshaler = (*MockDNSProvider)(nil)
|
||||||
|
var _ certmagic.DNSProvider = (*MockDNSProvider)(nil)
|
||||||
|
var _ caddy.Provisioner = (*MockDNSProvider)(nil)
|
||||||
|
var _ caddy.Module = (*MockDNSProvider)(nil)
|
|
@ -65,6 +65,12 @@ type AutoHTTPSConfig struct {
|
||||||
// enabled. To force automated certificate management
|
// enabled. To force automated certificate management
|
||||||
// regardless of loaded certificates, set this to true.
|
// regardless of loaded certificates, set this to true.
|
||||||
IgnoreLoadedCerts bool `json:"ignore_loaded_certificates,omitempty"`
|
IgnoreLoadedCerts bool `json:"ignore_loaded_certificates,omitempty"`
|
||||||
|
|
||||||
|
// If true, automatic HTTPS will prefer wildcard names
|
||||||
|
// and ignore non-wildcard names if both are available.
|
||||||
|
// This allows for writing a config with top-level host
|
||||||
|
// matchers without having those names produce certificates.
|
||||||
|
PreferWildcard bool `json:"prefer_wildcard,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// automaticHTTPSPhase1 provisions all route matchers, determines
|
// automaticHTTPSPhase1 provisions all route matchers, determines
|
||||||
|
@ -157,6 +163,27 @@ func (app *App) automaticHTTPSPhase1(ctx caddy.Context, repl *caddy.Replacer) er
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if srv.AutoHTTPS.PreferWildcard {
|
||||||
|
wildcards := make(map[string]struct{})
|
||||||
|
for d := range serverDomainSet {
|
||||||
|
if strings.HasPrefix(d, "*.") {
|
||||||
|
wildcards[d[2:]] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for d := range serverDomainSet {
|
||||||
|
if strings.HasPrefix(d, "*.") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
base := d
|
||||||
|
if idx := strings.Index(d, "."); idx != -1 {
|
||||||
|
base = d[idx+1:]
|
||||||
|
}
|
||||||
|
if _, ok := wildcards[base]; ok {
|
||||||
|
delete(serverDomainSet, d)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// nothing more to do here if there are no domains that qualify for
|
// nothing more to do here if there are no domains that qualify for
|
||||||
// automatic HTTPS and there are no explicit TLS connection policies:
|
// automatic HTTPS and there are no explicit TLS connection policies:
|
||||||
// if there is at least one domain but no TLS conn policy (F&&T), we'll
|
// if there is at least one domain but no TLS conn policy (F&&T), we'll
|
||||||
|
|
Loading…
Reference in a new issue