Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

caddytls: Add support for ZeroSSL; add Caddyfile support for issuers #3633

Merged
merged 10 commits into from
Aug 11, 2020
50 changes: 43 additions & 7 deletions caddyconfig/httpcaddyfile/builtins.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import (
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
"github.com/caddyserver/caddy/v2/modules/caddytls"
"github.com/caddyserver/certmagic"
"github.com/mholt/acmez/acme"
"go.uber.org/zap/zapcore"
)
Expand Down Expand Up @@ -76,6 +77,7 @@ func parseBind(h Helper) ([]ConfigValue, error) {
// ca_root <pem_file>
// dns <provider_name>
// on_demand
// issuer <module_name> ...
// }
//
func parseTLS(h Helper) ([]ConfigValue, error) {
Expand All @@ -85,6 +87,7 @@ func parseTLS(h Helper) ([]ConfigValue, error) {
var certSelector caddytls.CustomCertSelectionPolicy
var acmeIssuer *caddytls.ACMEIssuer
var internalIssuer *caddytls.InternalIssuer
var issuer certmagic.Issuer
var onDemand bool

for h.Next() {
Expand Down Expand Up @@ -276,6 +279,28 @@ func parseTLS(h Helper) ([]ConfigValue, error) {
MACKey: arg[1],
}

case "issuer":
if !h.NextArg() {
return nil, h.ArgErr()
}
modName := h.Val()
mod, err := caddy.GetModule("tls.issuance." + modName)
if err != nil {
return nil, h.Errf("getting issuer module '%s': %v", modName, err)
}
unm, ok := mod.New().(caddyfile.Unmarshaler)
if !ok {
return nil, h.Errf("issuer module '%s' is not a Caddyfile unmarshaler", mod.ID)
}
err = unm.UnmarshalCaddyfile(h.NewFromNextSegment())
if err != nil {
return nil, err
}
issuer, ok = unm.(certmagic.Issuer)
if !ok {
return nil, h.Errf("module %s is not a certmagic.Issuer", mod.ID)
}

case "dns":
if !h.NextArg() {
return nil, h.ArgErr()
Expand Down Expand Up @@ -350,7 +375,24 @@ func parseTLS(h Helper) ([]ConfigValue, error) {
// the logic to support this would be complex
return nil, h.Err("cannot use both ACME and internal issuers in same server block")
}
if acmeIssuer != nil {
if issuer != nil && (acmeIssuer != nil || internalIssuer != nil) {
// similarly, the logic to support this would be complex
return nil, h.Err("when defining an issuer, all its config must be in its block, rather than from separate tls subdirectives")
}
switch {
case issuer != nil:
configVals = append(configVals, ConfigValue{
Class: "tls.cert_issuer",
Value: issuer,
})

case internalIssuer != nil:
configVals = append(configVals, ConfigValue{
Class: "tls.cert_issuer",
Value: internalIssuer,
})

case acmeIssuer != nil:
// fill in global defaults, if configured
if email := h.Option("email"); email != nil && acmeIssuer.Email == "" {
acmeIssuer.Email = email.(string)
Expand All @@ -361,16 +403,10 @@ func parseTLS(h Helper) ([]ConfigValue, error) {
if caPemFile := h.Option("acme_ca_root"); caPemFile != nil {
acmeIssuer.TrustedRootsPEMFiles = append(acmeIssuer.TrustedRootsPEMFiles, caPemFile.(string))
}

configVals = append(configVals, ConfigValue{
Class: "tls.cert_issuer",
Value: acmeIssuer,
})
} else if internalIssuer != nil {
configVals = append(configVals, ConfigValue{
Class: "tls.cert_issuer",
Value: internalIssuer,
})
}

// on-demand TLS
Expand Down
29 changes: 29 additions & 0 deletions caddyconfig/httpcaddyfile/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"github.com/caddyserver/caddy/v2"
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
"github.com/caddyserver/caddy/v2/modules/caddytls"
"github.com/caddyserver/certmagic"
"github.com/mholt/acmez/acme"
)

Expand All @@ -35,6 +36,7 @@ func init() {
RegisterGlobalOption("acme_ca_root", parseOptSingleString)
RegisterGlobalOption("acme_dns", parseOptSingleString)
RegisterGlobalOption("acme_eab", parseOptACMEEAB)
RegisterGlobalOption("cert_issuer", parseOptCertIssuer)
RegisterGlobalOption("email", parseOptSingleString)
RegisterGlobalOption("admin", parseOptAdmin)
RegisterGlobalOption("on_demand_tls", parseOptOnDemand)
Expand Down Expand Up @@ -210,6 +212,33 @@ func parseOptACMEEAB(d *caddyfile.Dispenser) (interface{}, error) {
return eab, nil
}

func parseOptCertIssuer(d *caddyfile.Dispenser) (interface{}, error) {
if !d.Next() { // consume option name
return nil, d.ArgErr()
}
if !d.Next() { // get issuer module name
return nil, d.ArgErr()
}
modName := d.Val()
mod, err := caddy.GetModule("tls.issuance." + modName)
if err != nil {
return nil, d.Errf("getting issuer module '%s': %v", modName, err)
}
unm, ok := mod.New().(caddyfile.Unmarshaler)
if !ok {
return nil, d.Errf("issuer module '%s' is not a Caddyfile unmarshaler", mod.ID)
}
err = unm.UnmarshalCaddyfile(d.NewFromNextSegment())
if err != nil {
return nil, err
}
iss, ok := unm.(certmagic.Issuer)
if !ok {
return nil, d.Errf("module %s is not a certmagic.Issuer", mod.ID)
}
return iss, nil
}

func parseOptSingleString(d *caddyfile.Dispenser) (interface{}, error) {
d.Next() // consume parameter name
if !d.Next() {
Expand Down
24 changes: 17 additions & 7 deletions caddyconfig/httpcaddyfile/tlsapp.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,8 +135,11 @@ func (st ServerType) buildTLSApp(
// issuer, skip, since we intend to adjust only ACME issuers
var acmeIssuer *caddytls.ACMEIssuer
if ap.Issuer != nil {
var ok bool
if acmeIssuer, ok = ap.Issuer.(*caddytls.ACMEIssuer); !ok {
// ensure we include any issuer that embeds/wraps an underlying ACME issuer
type acmeCapable interface{ GetACMEIssuer() *caddytls.ACMEIssuer }
if acmeWrapper, ok := ap.Issuer.(acmeCapable); ok {
acmeIssuer = acmeWrapper.GetACMEIssuer()
} else {
break
}
}
Expand Down Expand Up @@ -348,6 +351,8 @@ func (st ServerType) buildTLSApp(
// returned if there are no default/global options. However, if always is
// true, a non-nil value will always be returned (unless there is an error).
func newBaseAutomationPolicy(options map[string]interface{}, warnings []caddyconfig.Warning, always bool) (*caddytls.AutomationPolicy, error) {
issuer, hasIssuer := options["cert_issuer"]

acmeCA, hasACMECA := options["acme_ca"]
acmeCARoot, hasACMECARoot := options["acme_ca_root"]
acmeDNS, hasACMEDNS := options["acme_dns"]
Expand All @@ -357,7 +362,7 @@ func newBaseAutomationPolicy(options map[string]interface{}, warnings []caddycon
localCerts, hasLocalCerts := options["local_certs"]
keyType, hasKeyType := options["key_type"]

hasGlobalAutomationOpts := hasACMECA || hasACMECARoot || hasACMEDNS || hasACMEEAB || hasEmail || hasLocalCerts || hasKeyType
hasGlobalAutomationOpts := hasIssuer || hasACMECA || hasACMECARoot || hasACMEDNS || hasACMEEAB || hasEmail || hasLocalCerts || hasKeyType

// if there are no global options related to automation policies
// set, then we can just return right away
Expand All @@ -369,8 +374,16 @@ func newBaseAutomationPolicy(options map[string]interface{}, warnings []caddycon
}

ap := new(caddytls.AutomationPolicy)
if keyType != nil {
ap.KeyType = keyType.(string)
}

if localCerts != nil {
if hasIssuer {
if hasACMECA || hasACMEDNS || hasACMEEAB || hasEmail || hasLocalCerts {
return nil, fmt.Errorf("global options are ambiguous: cert_issuer is confusing combined with acme_*, email, or local_certs options")
}
ap.Issuer = issuer.(certmagic.Issuer)
} else if localCerts != nil {
// internal issuer enabled trumps any ACME configurations; useful in testing
ap.Issuer = new(caddytls.InternalIssuer) // we'll encode it later
} else {
Expand Down Expand Up @@ -402,9 +415,6 @@ func newBaseAutomationPolicy(options map[string]interface{}, warnings []caddycon
if acmeEAB != nil {
mgr.ExternalAccount = acmeEAB.(*acme.EAB)
}
if keyType != nil {
ap.KeyType = keyType.(string)
}
ap.Issuer = mgr // we'll encode it later
}

Expand Down
3 changes: 2 additions & 1 deletion caddytest/integration/caddyfile_adapt/global_options.txt
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@
{
"issuer": {
"module": "internal"
}
},
"key_type": "ed25519"
}
],
"on_demand": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,8 @@
{
"issuer": {
"module": "internal"
}
},
"key_type": "ed25519"
}
],
"on_demand": {
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ require (
github.com/Masterminds/sprig/v3 v3.1.0
github.com/alecthomas/chroma v0.7.4-0.20200517063913-500529fd43c1
github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a
github.com/caddyserver/certmagic v0.11.3-0.20200730200704-7d9dfc3fe638
github.com/caddyserver/certmagic v0.11.3-0.20200804205533-c6afa6e7a22e
github.com/dustin/go-humanize v1.0.1-0.20200219035652-afde56e7acac
github.com/go-chi/chi v4.1.2+incompatible
github.com/google/cel-go v0.5.1
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,8 @@ github.com/bombsimon/wsl/v2 v2.0.0/go.mod h1:mf25kr/SqFEPhhcxW1+7pxzGlW+hIl/hYTK
github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
github.com/caddyserver/certmagic v0.11.3-0.20200730200704-7d9dfc3fe638 h1:yG2XwuzF6kQni6HscKZRj6Qr6mrpXsRTUzl0m+zH6o0=
github.com/caddyserver/certmagic v0.11.3-0.20200730200704-7d9dfc3fe638/go.mod h1:z0loWzjr6Sr8rOG2pdsiwajDpAcck2wfF8E7hTV0qT4=
github.com/caddyserver/certmagic v0.11.3-0.20200804205533-c6afa6e7a22e h1:R/EmX2RTXml60AI685PL+eXA+q5w0HKZDHEXYhIIlR8=
github.com/caddyserver/certmagic v0.11.3-0.20200804205533-c6afa6e7a22e/go.mod h1:z0loWzjr6Sr8rOG2pdsiwajDpAcck2wfF8E7hTV0qT4=
github.com/census-instrumentation/opencensus-proto v0.2.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
Expand Down
16 changes: 11 additions & 5 deletions modules/caddyhttp/autohttps.go
Original file line number Diff line number Diff line change
Expand Up @@ -451,8 +451,8 @@ func (app *App) createAutomationPolicies(ctx caddy.Context, publicNames, interna
if ap.Issuer == nil {
ap.Issuer = new(caddytls.ACMEIssuer)
}
if acmeIssuer, ok := ap.Issuer.(*caddytls.ACMEIssuer); ok {
err := app.fillInACMEIssuer(acmeIssuer)
if acmeIssuer, ok := ap.Issuer.(acmeCapable); ok {
err := app.fillInACMEIssuer(acmeIssuer.GetACMEIssuer())
if err != nil {
return err
}
Expand All @@ -470,9 +470,13 @@ func (app *App) createAutomationPolicies(ctx caddy.Context, publicNames, interna
basePolicy = new(caddytls.AutomationPolicy)
}

// if the basePolicy has an existing ACMEIssuer, let's
// use it, otherwise we'll make one
baseACMEIssuer, _ := basePolicy.Issuer.(*caddytls.ACMEIssuer)
// if the basePolicy has an existing ACMEIssuer (particularly to
// include any type that embeds/wraps an ACMEIssuer), let's use it,
// otherwise we'll make one
var baseACMEIssuer *caddytls.ACMEIssuer
if acmeWrapper, ok := basePolicy.Issuer.(acmeCapable); ok {
baseACMEIssuer = acmeWrapper.GetACMEIssuer()
}
if baseACMEIssuer == nil {
// note that this happens if basePolicy.Issuer is nil
// OR if it is not nil but is not an ACMEIssuer
Expand Down Expand Up @@ -630,3 +634,5 @@ func (app *App) automaticHTTPSPhase2() error {
app.allCertDomains = nil // no longer needed; allow GC to deallocate
return nil
}

type acmeCapable interface{ GetACMEIssuer() *caddytls.ACMEIssuer }
Loading