httpcaddyfile: Add preferred_chains global option and issuer subdirective (#4192)

* Added preferred_chains option to Caddyfile

* Caddyfile adapt tests for preferred_chains
This commit is contained in:
Klooven 2021-06-08 23:10:37 +03:00 committed by GitHub
parent 76913b19ff
commit 1e92258dd6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 187 additions and 1 deletions

View file

@ -49,6 +49,7 @@ func init() {
RegisterGlobalOption("servers", parseServerOptions)
RegisterGlobalOption("ocsp_stapling", parseOCSPStaplingOptions)
RegisterGlobalOption("log", parseLogOptions)
RegisterGlobalOption("preferred_chains", parseOptPreferredChains)
}
func parseOptTrue(d *caddyfile.Dispenser, _ interface{}) (interface{}, error) { return true, nil }
@ -452,3 +453,8 @@ func parseLogOptions(d *caddyfile.Dispenser, existingVal interface{}) (interface
return configValues, nil
}
func parseOptPreferredChains(d *caddyfile.Dispenser, _ interface{}) (interface{}, error) {
d.Next()
return caddytls.ParseCaddyfilePreferredChainsOptions(d)
}

View file

@ -321,7 +321,8 @@ func (st ServerType) buildTLSApp(
globalACMECARoot := options["acme_ca_root"]
globalACMEDNS := options["acme_dns"]
globalACMEEAB := options["acme_eab"]
hasGlobalACMEDefaults := globalEmail != nil || globalACMECA != nil || globalACMECARoot != nil || globalACMEDNS != nil || globalACMEEAB != nil
globalPreferredChains := options["preferred_chains"]
hasGlobalACMEDefaults := globalEmail != nil || globalACMECA != nil || globalACMECARoot != nil || globalACMEDNS != nil || globalACMEEAB != nil || globalPreferredChains != nil
if hasGlobalACMEDefaults {
for _, ap := range tlsApp.Automation.Policies {
if len(ap.Issuers) == 0 {
@ -405,6 +406,7 @@ func fillInGlobalACMEDefaults(issuer certmagic.Issuer, options map[string]interf
globalACMECARoot := options["acme_ca_root"]
globalACMEDNS := options["acme_dns"]
globalACMEEAB := options["acme_eab"]
globalPreferredChains := options["preferred_chains"]
if globalEmail != nil && acmeIssuer.Email == "" {
acmeIssuer.Email = globalEmail.(string)
@ -425,6 +427,9 @@ func fillInGlobalACMEDefaults(issuer certmagic.Issuer, options map[string]interf
if globalACMEEAB != nil && acmeIssuer.ExternalAccount == nil {
acmeIssuer.ExternalAccount = globalACMEEAB.(*acme.EAB)
}
if globalPreferredChains != nil && acmeIssuer.PreferredChains == nil {
acmeIssuer.PreferredChains = globalPreferredChains.(*caddytls.ChainPreference)
}
return nil
}

View file

@ -0,0 +1,56 @@
{
preferred_chains smallest
}
localhost
----------
{
"apps": {
"http": {
"servers": {
"srv0": {
"listen": [
":443"
],
"routes": [
{
"match": [
{
"host": [
"localhost"
]
}
],
"terminal": true
}
]
}
}
},
"tls": {
"automation": {
"policies": [
{
"subjects": [
"localhost"
],
"issuers": [
{
"module": "acme",
"preferred_chains": {
"smallest": true
}
},
{
"module": "zerossl",
"preferred_chains": {
"smallest": true
}
}
]
}
]
}
}
}
}

View file

@ -0,0 +1,57 @@
localhost
tls {
issuer acme {
preferred_chains {
any_common_name "Generic CA 1" "Generic CA 2"
}
}
}
----------
{
"apps": {
"http": {
"servers": {
"srv0": {
"listen": [
":443"
],
"routes": [
{
"match": [
{
"host": [
"localhost"
]
}
],
"terminal": true
}
]
}
}
},
"tls": {
"automation": {
"policies": [
{
"subjects": [
"localhost"
],
"issuers": [
{
"module": "acme",
"preferred_chains": {
"any_common_name": [
"Generic CA 1",
"Generic CA 2"
]
}
}
]
}
]
}
}
}
}

View file

@ -265,6 +265,10 @@ func (iss *ACMEIssuer) GetACMEIssuer() *ACMEIssuer { return iss }
// trusted_roots <pem_files...>
// dns <provider_name> [<options>]
// resolvers <dns_servers...>
// preferred_chains [smallest] {
// root_common_name <common_names...>
// any_common_name <common_names...>
// }
// }
//
func (iss *ACMEIssuer) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
@ -416,6 +420,13 @@ func (iss *ACMEIssuer) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
return d.ArgErr()
}
case "preferred_chains":
chainPref, err := ParseCaddyfilePreferredChainsOptions(d)
if err != nil {
return err
}
iss.PreferredChains = chainPref
default:
return d.Errf("unrecognized ACME issuer property: %s", d.Val())
}
@ -452,6 +463,57 @@ func onDemandAskRequest(ask string, name string) error {
return nil
}
func ParseCaddyfilePreferredChainsOptions(d *caddyfile.Dispenser) (*ChainPreference, error) {
chainPref := new(ChainPreference)
if d.NextArg() {
smallestOpt := d.Val()
if smallestOpt == "smallest" {
trueBool := true
chainPref.Smallest = &trueBool
if d.NextArg() { // Only one argument allowed
return nil, d.ArgErr()
}
if d.NextBlock(d.Nesting()) { // Don't allow other options when smallest == true
return nil, d.Err("No more options are accepted when using the 'smallest' option")
}
} else { // Smallest option should always be 'smallest' or unset
return nil, d.Errf("Invalid argument '%s'", smallestOpt)
}
}
for nesting := d.Nesting(); d.NextBlock(nesting); {
switch d.Val() {
case "root_common_name":
rootCommonNameOpt := d.RemainingArgs()
chainPref.RootCommonName = rootCommonNameOpt
if rootCommonNameOpt == nil {
return nil, d.ArgErr()
}
if chainPref.AnyCommonName != nil {
return nil, d.Err("Can't set root_common_name when any_common_name is already set")
}
case "any_common_name":
anyCommonNameOpt := d.RemainingArgs()
chainPref.AnyCommonName = anyCommonNameOpt
if anyCommonNameOpt == nil {
return nil, d.ArgErr()
}
if chainPref.RootCommonName != nil {
return nil, d.Err("Can't set any_common_name when root_common_name is already set")
}
default:
return nil, d.Errf("Received unrecognized parameter '%s'", d.Val())
}
}
if chainPref.Smallest == nil && chainPref.RootCommonName == nil && chainPref.AnyCommonName == nil {
return nil, d.Err("No options for preferred_chains received")
}
return chainPref, nil
}
// ChainPreference describes the client's preferred certificate chain,
// useful if the CA offers alternate chains. The first matching chain
// will be selected.