From 4b9849c7922c3a0a7b1bd487f5d890fcff32aaba Mon Sep 17 00:00:00 2001 From: Francis Lavoie Date: Wed, 5 Jan 2022 22:45:41 -0500 Subject: [PATCH] httpcaddyfile: Support configuring `pki` app names via global options (#4450) --- caddyconfig/httpcaddyfile/pkiapp.go | 96 ++++++++++++++++++- .../global_options_skip_install_trust.txt | 92 ++++++++++++++++++ 2 files changed, 185 insertions(+), 3 deletions(-) diff --git a/caddyconfig/httpcaddyfile/pkiapp.go b/caddyconfig/httpcaddyfile/pkiapp.go index a21951db154..b1aac75fafb 100644 --- a/caddyconfig/httpcaddyfile/pkiapp.go +++ b/caddyconfig/httpcaddyfile/pkiapp.go @@ -16,23 +16,108 @@ package httpcaddyfile import ( "github.com/caddyserver/caddy/v2/caddyconfig" + "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile" "github.com/caddyserver/caddy/v2/modules/caddypki" ) +func init() { + RegisterGlobalOption("pki", parsePKIApp) +} + +// parsePKIApp parses the global log option. Syntax: +// +// pki { +// ca [] { +// name +// root_cn +// intermediate_cn +// } +// } +// +// When the CA ID is unspecified, 'local' is assumed. +// +func parsePKIApp(d *caddyfile.Dispenser, existingVal interface{}) (interface{}, error) { + pki := &caddypki.PKI{CAs: make(map[string]*caddypki.CA)} + + for d.Next() { + for nesting := d.Nesting(); d.NextBlock(nesting); { + switch d.Val() { + case "ca": + pkiCa := new(caddypki.CA) + if d.NextArg() { + pkiCa.ID = d.Val() + if d.NextArg() { + return nil, d.ArgErr() + } + } + if pkiCa.ID == "" { + pkiCa.ID = caddypki.DefaultCAID + } + + for nesting := d.Nesting(); d.NextBlock(nesting); { + switch d.Val() { + case "name": + if !d.NextArg() { + return nil, d.ArgErr() + } + pkiCa.Name = d.Val() + + case "root_cn": + if !d.NextArg() { + return nil, d.ArgErr() + } + pkiCa.RootCommonName = d.Val() + + case "intermediate_cn": + if !d.NextArg() { + return nil, d.ArgErr() + } + pkiCa.IntermediateCommonName = d.Val() + + default: + return nil, d.Errf("unrecognized pki ca option '%s'", d.Val()) + } + } + + pki.CAs[pkiCa.ID] = pkiCa + + default: + return nil, d.Errf("unrecognized pki option '%s'", d.Val()) + } + } + } + + return pki, nil +} + func (st ServerType) buildPKIApp( pairings []sbAddrAssociation, options map[string]interface{}, warnings []caddyconfig.Warning, ) (*caddypki.PKI, []caddyconfig.Warning, error) { - pkiApp := &caddypki.PKI{CAs: make(map[string]*caddypki.CA)} - skipInstallTrust := false if _, ok := options["skip_install_trust"]; ok { skipInstallTrust = true } falseBool := false + // Load the PKI app configured via global options + var pkiApp *caddypki.PKI + unwrappedPki, ok := options["pki"].(*caddypki.PKI) + if ok { + pkiApp = unwrappedPki + } else { + pkiApp = &caddypki.PKI{CAs: make(map[string]*caddypki.CA)} + } + for _, ca := range pkiApp.CAs { + if skipInstallTrust { + ca.InstallTrust = &falseBool + } + pkiApp.CAs[ca.ID] = ca + } + + // Add in the CAs configured via directives for _, p := range pairings { for _, sblock := range p.serverBlocks { // find all the CAs that were defined and add them to the app config @@ -42,7 +127,12 @@ func (st ServerType) buildPKIApp( if skipInstallTrust { ca.InstallTrust = &falseBool } - pkiApp.CAs[ca.ID] = ca + + // the CA might already exist from global options, so + // don't overwrite it in that case + if _, ok := pkiApp.CAs[ca.ID]; !ok { + pkiApp.CAs[ca.ID] = ca + } } } } diff --git a/caddytest/integration/caddyfile_adapt/global_options_skip_install_trust.txt b/caddytest/integration/caddyfile_adapt/global_options_skip_install_trust.txt index f949ac1af75..39c118ff8ac 100644 --- a/caddytest/integration/caddyfile_adapt/global_options_skip_install_trust.txt +++ b/caddytest/integration/caddyfile_adapt/global_options_skip_install_trust.txt @@ -1,10 +1,34 @@ { skip_install_trust + pki { + ca { + name "Local" + root_cn "Custom Local Root Name" + intermediate_cn "Custom Local Intermediate Name" + } + ca foo { + name "Foo" + root_cn "Custom Foo Root Name" + intermediate_cn "Custom Foo Intermediate Name" + } + } } a.example.com { tls internal } + +acme.example.com { + acme_server { + ca foo + } +} + +acme-bar.example.com { + acme_server { + ca bar + } +} ---------- { "apps": { @@ -15,6 +39,56 @@ a.example.com { ":443" ], "routes": [ + { + "match": [ + { + "host": [ + "acme-bar.example.com" + ] + } + ], + "handle": [ + { + "handler": "subroute", + "routes": [ + { + "handle": [ + { + "ca": "bar", + "handler": "acme_server" + } + ] + } + ] + } + ], + "terminal": true + }, + { + "match": [ + { + "host": [ + "acme.example.com" + ] + } + ], + "handle": [ + { + "handler": "subroute", + "routes": [ + { + "handle": [ + { + "ca": "foo", + "handler": "acme_server" + } + ] + } + ] + } + ], + "terminal": true + }, { "match": [ { @@ -31,7 +105,19 @@ a.example.com { }, "pki": { "certificate_authorities": { + "bar": { + "install_trust": false + }, + "foo": { + "name": "Foo", + "root_common_name": "Custom Foo Root Name", + "intermediate_common_name": "Custom Foo Intermediate Name", + "install_trust": false + }, "local": { + "name": "Local", + "root_common_name": "Custom Local Root Name", + "intermediate_common_name": "Custom Local Intermediate Name", "install_trust": false } } @@ -39,6 +125,12 @@ a.example.com { "tls": { "automation": { "policies": [ + { + "subjects": [ + "acme-bar.example.com", + "acme.example.com" + ] + }, { "subjects": [ "a.example.com"