diff options
Diffstat (limited to 'modules/caddytls/acmeissuer.go')
-rw-r--r-- | modules/caddytls/acmeissuer.go | 221 |
1 files changed, 174 insertions, 47 deletions
diff --git a/modules/caddytls/acmeissuer.go b/modules/caddytls/acmeissuer.go index 8fe308d..963143b 100644 --- a/modules/caddytls/acmeissuer.go +++ b/modules/caddytls/acmeissuer.go @@ -20,9 +20,11 @@ import ( "fmt" "io/ioutil" "net/url" + "strconv" "time" "github.com/caddyserver/caddy/v2" + "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile" "github.com/caddyserver/certmagic" "github.com/mholt/acmez" "github.com/mholt/acmez/acme" @@ -85,13 +87,13 @@ func (ACMEIssuer) CaddyModule() caddy.ModuleInfo { } } -// Provision sets up m. -func (m *ACMEIssuer) Provision(ctx caddy.Context) error { - m.logger = ctx.Logger(m) +// Provision sets up iss. +func (iss *ACMEIssuer) Provision(ctx caddy.Context) error { + iss.logger = ctx.Logger(iss) // DNS providers - if m.Challenges != nil && m.Challenges.DNS != nil && m.Challenges.DNS.ProviderRaw != nil { - val, err := ctx.LoadModule(m.Challenges.DNS, "ProviderRaw") + if iss.Challenges != nil && iss.Challenges.DNS != nil && iss.Challenges.DNS.ProviderRaw != nil { + val, err := ctx.LoadModule(iss.Challenges.DNS, "ProviderRaw") if err != nil { return fmt.Errorf("loading DNS provider module: %v", err) } @@ -104,32 +106,32 @@ func (m *ACMEIssuer) Provision(ctx caddy.Context) error { // acmez.Solver type, so we use it directly. The user must set environment // variables to configure it. Remove this shim once a sufficient number of // DNS providers are implemented for the libdns APIs instead. - m.Challenges.DNS.solver = deprecatedProvider + iss.Challenges.DNS.solver = deprecatedProvider } else { - m.Challenges.DNS.solver = &certmagic.DNS01Solver{ + iss.Challenges.DNS.solver = &certmagic.DNS01Solver{ DNSProvider: val.(certmagic.ACMEDNSProvider), - TTL: time.Duration(m.Challenges.DNS.TTL), - PropagationTimeout: time.Duration(m.Challenges.DNS.PropagationTimeout), + TTL: time.Duration(iss.Challenges.DNS.TTL), + PropagationTimeout: time.Duration(iss.Challenges.DNS.PropagationTimeout), } } } // add any custom CAs to trust store - if len(m.TrustedRootsPEMFiles) > 0 { - m.rootPool = x509.NewCertPool() - for _, pemFile := range m.TrustedRootsPEMFiles { + if len(iss.TrustedRootsPEMFiles) > 0 { + iss.rootPool = x509.NewCertPool() + for _, pemFile := range iss.TrustedRootsPEMFiles { pemData, err := ioutil.ReadFile(pemFile) if err != nil { return fmt.Errorf("loading trusted root CA's PEM file: %s: %v", pemFile, err) } - if !m.rootPool.AppendCertsFromPEM(pemData) { + if !iss.rootPool.AppendCertsFromPEM(pemData) { return fmt.Errorf("unable to add %s to trust pool: %v", pemFile, err) } } } var err error - m.template, err = m.makeIssuerTemplate() + iss.template, err = iss.makeIssuerTemplate() if err != nil { return err } @@ -137,30 +139,30 @@ func (m *ACMEIssuer) Provision(ctx caddy.Context) error { return nil } -func (m *ACMEIssuer) makeIssuerTemplate() (certmagic.ACMEManager, error) { +func (iss *ACMEIssuer) makeIssuerTemplate() (certmagic.ACMEManager, error) { template := certmagic.ACMEManager{ - CA: m.CA, - TestCA: m.TestCA, - Email: m.Email, - CertObtainTimeout: time.Duration(m.ACMETimeout), - TrustedRoots: m.rootPool, - ExternalAccount: m.ExternalAccount, - Logger: m.logger, + CA: iss.CA, + TestCA: iss.TestCA, + Email: iss.Email, + CertObtainTimeout: time.Duration(iss.ACMETimeout), + TrustedRoots: iss.rootPool, + ExternalAccount: iss.ExternalAccount, + Logger: iss.logger, } - if m.Challenges != nil { - if m.Challenges.HTTP != nil { - template.DisableHTTPChallenge = m.Challenges.HTTP.Disabled - template.AltHTTPPort = m.Challenges.HTTP.AlternatePort + if iss.Challenges != nil { + if iss.Challenges.HTTP != nil { + template.DisableHTTPChallenge = iss.Challenges.HTTP.Disabled + template.AltHTTPPort = iss.Challenges.HTTP.AlternatePort } - if m.Challenges.TLSALPN != nil { - template.DisableTLSALPNChallenge = m.Challenges.TLSALPN.Disabled - template.AltTLSALPNPort = m.Challenges.TLSALPN.AlternatePort + if iss.Challenges.TLSALPN != nil { + template.DisableTLSALPNChallenge = iss.Challenges.TLSALPN.Disabled + template.AltTLSALPNPort = iss.Challenges.TLSALPN.AlternatePort } - if m.Challenges.DNS != nil { - template.DNS01Solver = m.Challenges.DNS.solver + if iss.Challenges.DNS != nil { + template.DNS01Solver = iss.Challenges.DNS.solver } - template.ListenHost = m.Challenges.BindHost + template.ListenHost = iss.Challenges.BindHost } return template, nil @@ -170,8 +172,8 @@ func (m *ACMEIssuer) makeIssuerTemplate() (certmagic.ACMEManager, error) { // This is required because ACME needs values from the config in // order to solve the challenges during issuance. This implements // the ConfigSetter interface. -func (m *ACMEIssuer) SetConfig(cfg *certmagic.Config) { - m.magic = cfg +func (iss *ACMEIssuer) SetConfig(cfg *certmagic.Config) { + iss.magic = cfg } // TODO: I kind of hate how each call to these methods needs to @@ -179,23 +181,147 @@ func (m *ACMEIssuer) SetConfig(cfg *certmagic.Config) { // we find the right place to do that just once and then re-use? // PreCheck implements the certmagic.PreChecker interface. -func (m *ACMEIssuer) PreCheck(ctx context.Context, names []string, interactive bool) error { - return certmagic.NewACMEManager(m.magic, m.template).PreCheck(ctx, names, interactive) +func (iss *ACMEIssuer) PreCheck(ctx context.Context, names []string, interactive bool) error { + return certmagic.NewACMEManager(iss.magic, iss.template).PreCheck(ctx, names, interactive) } // Issue obtains a certificate for the given csr. -func (m *ACMEIssuer) Issue(ctx context.Context, csr *x509.CertificateRequest) (*certmagic.IssuedCertificate, error) { - return certmagic.NewACMEManager(m.magic, m.template).Issue(ctx, csr) +func (iss *ACMEIssuer) Issue(ctx context.Context, csr *x509.CertificateRequest) (*certmagic.IssuedCertificate, error) { + return certmagic.NewACMEManager(iss.magic, iss.template).Issue(ctx, csr) } // IssuerKey returns the unique issuer key for the configured CA endpoint. -func (m *ACMEIssuer) IssuerKey() string { - return certmagic.NewACMEManager(m.magic, m.template).IssuerKey() +func (iss *ACMEIssuer) IssuerKey() string { + return certmagic.NewACMEManager(iss.magic, iss.template).IssuerKey() } // Revoke revokes the given certificate. -func (m *ACMEIssuer) Revoke(ctx context.Context, cert certmagic.CertificateResource, reason int) error { - return certmagic.NewACMEManager(m.magic, m.template).Revoke(ctx, cert, reason) +func (iss *ACMEIssuer) Revoke(ctx context.Context, cert certmagic.CertificateResource, reason int) error { + return certmagic.NewACMEManager(iss.magic, iss.template).Revoke(ctx, cert, reason) +} + +// GetACMEIssuer returns iss. This is useful when other types embed ACMEIssuer, because +// type-asserting them to *ACMEIssuer will fail, but type-asserting them to an interface +// with only this method will succeed, and will still allow the embedded ACMEIssuer +// to be accessed and manipulated. +func (iss *ACMEIssuer) GetACMEIssuer() *ACMEIssuer { return iss } + +// UnmarshalCaddyfile deserializes Caddyfile tokens into iss. +// +// ... acme { +// dir <directory_url> +// test_dir <test_directory_url> +// email <email> +// timeout <duration> +// disable_http_challenge +// disable_tlsalpn_challenge +// alt_http_port <port> +// alt_tlsalpn_port <port> +// eab <key_id> <mac_key> +// trusted_roots <pem_files...> +// } +// +func (iss *ACMEIssuer) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { + for d.Next() { + for nesting := d.Nesting(); d.NextBlock(nesting); { + switch d.Val() { + case "dir": + if !d.AllArgs(&iss.CA) { + return d.ArgErr() + } + + case "test_dir": + if !d.AllArgs(&iss.TestCA) { + return d.ArgErr() + } + + case "email": + if !d.AllArgs(&iss.Email) { + return d.ArgErr() + } + + case "timeout": + var timeoutStr string + if !d.AllArgs(&timeoutStr) { + return d.ArgErr() + } + timeout, err := caddy.ParseDuration(timeoutStr) + if err != nil { + return d.Errf("invalid timeout duration %s: %v", timeoutStr, err) + } + iss.ACMETimeout = caddy.Duration(timeout) + + case "disable_http_challenge": + if d.NextArg() { + return d.ArgErr() + } + if iss.Challenges == nil { + iss.Challenges = new(ChallengesConfig) + } + if iss.Challenges.HTTP == nil { + iss.Challenges.HTTP = new(HTTPChallengeConfig) + } + iss.Challenges.HTTP.Disabled = true + + case "disable_tlsalpn_challenge": + if d.NextArg() { + return d.ArgErr() + } + if iss.Challenges == nil { + iss.Challenges = new(ChallengesConfig) + } + if iss.Challenges.TLSALPN == nil { + iss.Challenges.TLSALPN = new(TLSALPNChallengeConfig) + } + iss.Challenges.TLSALPN.Disabled = true + + case "alt_http_port": + if !d.NextArg() { + return d.ArgErr() + } + port, err := strconv.Atoi(d.Val()) + if err != nil { + return d.Errf("invalid port %s: %v", d.Val(), err) + } + if iss.Challenges == nil { + iss.Challenges = new(ChallengesConfig) + } + if iss.Challenges.HTTP == nil { + iss.Challenges.HTTP = new(HTTPChallengeConfig) + } + iss.Challenges.HTTP.AlternatePort = port + + case "alt_tlsalpn_port": + if !d.NextArg() { + return d.ArgErr() + } + port, err := strconv.Atoi(d.Val()) + if err != nil { + return d.Errf("invalid port %s: %v", d.Val(), err) + } + if iss.Challenges == nil { + iss.Challenges = new(ChallengesConfig) + } + if iss.Challenges.TLSALPN == nil { + iss.Challenges.TLSALPN = new(TLSALPNChallengeConfig) + } + iss.Challenges.TLSALPN.AlternatePort = port + + case "eab": + iss.ExternalAccount = new(acme.EAB) + if !d.AllArgs(&iss.ExternalAccount.KeyID, &iss.ExternalAccount.MACKey) { + return d.ArgErr() + } + + case "trusted_roots": + iss.TrustedRootsPEMFiles = d.RemainingArgs() + + default: + return d.Errf("unrecognized ACME issuer property: %s", d.Val()) + } + } + } + return nil } // onDemandAskRequest makes a request to the ask URL @@ -228,9 +354,10 @@ func onDemandAskRequest(ask string, name string) error { // Interface guards var ( - _ certmagic.PreChecker = (*ACMEIssuer)(nil) - _ certmagic.Issuer = (*ACMEIssuer)(nil) - _ certmagic.Revoker = (*ACMEIssuer)(nil) - _ caddy.Provisioner = (*ACMEIssuer)(nil) - _ ConfigSetter = (*ACMEIssuer)(nil) + _ certmagic.PreChecker = (*ACMEIssuer)(nil) + _ certmagic.Issuer = (*ACMEIssuer)(nil) + _ certmagic.Revoker = (*ACMEIssuer)(nil) + _ caddy.Provisioner = (*ACMEIssuer)(nil) + _ ConfigSetter = (*ACMEIssuer)(nil) + _ caddyfile.Unmarshaler = (*ACMEIssuer)(nil) ) |