summaryrefslogtreecommitdiff
path: root/modules/caddytls
diff options
context:
space:
mode:
authorMatt Holt <mholt@users.noreply.github.com>2020-07-30 15:18:14 -0600
committerGitHub <noreply@github.com>2020-07-30 15:18:14 -0600
commit6a14e2c2a8881d5e90f1ee363ec4662a3f87402b (patch)
tree3d10bdfe62e709e70e16725659e860bee20ca8e6 /modules/caddytls
parent2bc30bb780f3b93593a2a9e42db6ab215fe12902 (diff)
caddytls: Replace lego with acmez (#3621)
* Replace lego with acmez; upgrade CertMagic * Update integration test
Diffstat (limited to 'modules/caddytls')
-rw-r--r--modules/caddytls/acmeissuer.go67
-rw-r--r--modules/caddytls/automation.go13
-rw-r--r--modules/caddytls/connpolicy.go12
-rw-r--r--modules/caddytls/dnssolver.go113
-rw-r--r--modules/caddytls/tls.go9
5 files changed, 50 insertions, 164 deletions
diff --git a/modules/caddytls/acmeissuer.go b/modules/caddytls/acmeissuer.go
index 195ddeb..8fe308d 100644
--- a/modules/caddytls/acmeissuer.go
+++ b/modules/caddytls/acmeissuer.go
@@ -24,7 +24,9 @@ import (
"github.com/caddyserver/caddy/v2"
"github.com/caddyserver/certmagic"
- "github.com/go-acme/lego/v3/challenge"
+ "github.com/mholt/acmez"
+ "github.com/mholt/acmez/acme"
+ "go.uber.org/zap"
)
func init() {
@@ -56,7 +58,7 @@ type ACMEIssuer struct {
// If using an ACME CA that requires an external account
// binding, specify the CA-provided credentials here.
- ExternalAccount *ExternalAccountBinding `json:"external_account,omitempty"`
+ ExternalAccount *acme.EAB `json:"external_account,omitempty"`
// Time to wait before timing out an ACME operation.
ACMETimeout caddy.Duration `json:"acme_timeout,omitempty"`
@@ -72,6 +74,7 @@ type ACMEIssuer struct {
rootPool *x509.CertPool
template certmagic.ACMEManager
magic *certmagic.Config
+ logger *zap.Logger
}
// CaddyModule returns the Caddy module information.
@@ -84,25 +87,29 @@ func (ACMEIssuer) CaddyModule() caddy.ModuleInfo {
// Provision sets up m.
func (m *ACMEIssuer) Provision(ctx caddy.Context) error {
+ m.logger = ctx.Logger(m)
+
// DNS providers
if m.Challenges != nil && m.Challenges.DNS != nil && m.Challenges.DNS.ProviderRaw != nil {
val, err := ctx.LoadModule(m.Challenges.DNS, "ProviderRaw")
if err != nil {
return fmt.Errorf("loading DNS provider module: %v", err)
}
- // TODO: For a temporary amount of time, we are allowing the use of
- // DNS providers from go-acme/lego since there are so many implemented
- // for it -- they are adapted as Caddy modules in this repository:
- // https://github.com/caddy-dns/lego-deprecated - that module is
- // a challenge.Provider value, 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.
- if grandfatheredProvider, ok := val.(challenge.Provider); ok {
- m.Challenges.DNS.provider = grandfatheredProvider
+
+ if deprecatedProvider, ok := val.(acmez.Solver); ok {
+ // TODO: For a temporary amount of time, we are allowing the use of DNS
+ // providers from go-acme/lego since there are so many providers implemented
+ // using that API -- they are adapted as an all-in-one Caddy module in this
+ // repository: https://github.com/caddy-dns/lego-deprecated - the module is a
+ // 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
} else {
- m.Challenges.DNS.provider = &solver{
- recordManager: val.(recordManager),
- TTL: time.Duration(m.Challenges.DNS.TTL),
+ m.Challenges.DNS.solver = &certmagic.DNS01Solver{
+ DNSProvider: val.(certmagic.ACMEDNSProvider),
+ TTL: time.Duration(m.Challenges.DNS.TTL),
+ PropagationTimeout: time.Duration(m.Challenges.DNS.PropagationTimeout),
}
}
}
@@ -137,16 +144,8 @@ func (m *ACMEIssuer) makeIssuerTemplate() (certmagic.ACMEManager, error) {
Email: m.Email,
CertObtainTimeout: time.Duration(m.ACMETimeout),
TrustedRoots: m.rootPool,
- }
-
- if m.ExternalAccount != nil {
- if m.ExternalAccount.KeyID == "" || m.ExternalAccount.HMAC == "" {
- return template, fmt.Errorf("when an external account binding is specified, both key ID and HMAC are required")
- }
- template.ExternalAccount = &certmagic.ExternalAccountBinding{
- KeyID: m.ExternalAccount.KeyID,
- HMAC: m.ExternalAccount.HMAC,
- }
+ ExternalAccount: m.ExternalAccount,
+ Logger: m.logger,
}
if m.Challenges != nil {
@@ -159,7 +158,7 @@ func (m *ACMEIssuer) makeIssuerTemplate() (certmagic.ACMEManager, error) {
template.AltTLSALPNPort = m.Challenges.TLSALPN.AlternatePort
}
if m.Challenges.DNS != nil {
- template.DNSProvider = m.Challenges.DNS.provider
+ template.DNS01Solver = m.Challenges.DNS.solver
}
template.ListenHost = m.Challenges.BindHost
}
@@ -180,8 +179,8 @@ 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(names []string, interactive bool) error {
- return certmagic.NewACMEManager(m.magic, m.template).PreCheck(names, interactive)
+func (m *ACMEIssuer) PreCheck(ctx context.Context, names []string, interactive bool) error {
+ return certmagic.NewACMEManager(m.magic, m.template).PreCheck(ctx, names, interactive)
}
// Issue obtains a certificate for the given csr.
@@ -195,8 +194,8 @@ func (m *ACMEIssuer) IssuerKey() string {
}
// Revoke revokes the given certificate.
-func (m *ACMEIssuer) Revoke(ctx context.Context, cert certmagic.CertificateResource) error {
- return certmagic.NewACMEManager(m.magic, m.template).Revoke(ctx, cert)
+func (m *ACMEIssuer) Revoke(ctx context.Context, cert certmagic.CertificateResource, reason int) error {
+ return certmagic.NewACMEManager(m.magic, m.template).Revoke(ctx, cert, reason)
}
// onDemandAskRequest makes a request to the ask URL
@@ -227,16 +226,6 @@ func onDemandAskRequest(ask string, name string) error {
return nil
}
-// ExternalAccountBinding contains information for
-// binding an external account to an ACME account.
-type ExternalAccountBinding struct {
- // The key identifier.
- KeyID string `json:"key_id,omitempty"`
-
- // The HMAC.
- HMAC string `json:"hmac,omitempty"`
-}
-
// Interface guards
var (
_ certmagic.PreChecker = (*ACMEIssuer)(nil)
diff --git a/modules/caddytls/automation.go b/modules/caddytls/automation.go
index 37d5010..502a631 100644
--- a/modules/caddytls/automation.go
+++ b/modules/caddytls/automation.go
@@ -22,12 +22,11 @@ import (
"github.com/caddyserver/caddy/v2"
"github.com/caddyserver/certmagic"
- "github.com/go-acme/lego/v3/challenge"
+ "github.com/mholt/acmez"
"go.uber.org/zap"
)
-// AutomationConfig designates configuration for the
-// construction and use of ACME clients.
+// AutomationConfig governs the automated management of TLS certificates.
type AutomationConfig struct {
// The list of automation policies. The first matching
// policy will be applied for a given certificate/name.
@@ -208,6 +207,7 @@ func (ap *AutomationPolicy) Provision(tlsApp *TLS) error {
OnDemand: ond,
Storage: storage,
Issuer: ap.Issuer, // if nil, certmagic.New() will create one
+ Logger: tlsApp.logger,
}
if rev, ok := ap.Issuer.(certmagic.Revoker); ok {
template.Revoker = rev
@@ -244,6 +244,7 @@ type ChallengesConfig struct {
// not enabled by default. This is the only challenge
// type which does not require a direct connection
// to Caddy from an external server.
+ //
// NOTE: DNS providers are currently being upgraded,
// and this API is subject to change, but should be
// stabilized soon.
@@ -281,6 +282,7 @@ type TLSALPNChallengeConfig struct {
}
// DNSChallengeConfig configures the ACME DNS challenge.
+//
// NOTE: This API is still experimental and is subject to change.
type DNSChallengeConfig struct {
// The DNS provider module to use which will manage
@@ -290,7 +292,10 @@ type DNSChallengeConfig struct {
// The TTL of the TXT record used for the DNS challenge.
TTL caddy.Duration `json:"ttl,omitempty"`
- provider challenge.Provider
+ // How long to wait for DNS record to propagate.
+ PropagationTimeout caddy.Duration `json:"propagation_timeout,omitempty"`
+
+ solver acmez.Solver
}
// OnDemandConfig configures on-demand TLS, for obtaining
diff --git a/modules/caddytls/connpolicy.go b/modules/caddytls/connpolicy.go
index fec1fe2..7eda002 100644
--- a/modules/caddytls/connpolicy.go
+++ b/modules/caddytls/connpolicy.go
@@ -22,12 +22,12 @@ import (
"strings"
"github.com/caddyserver/caddy/v2"
- "github.com/go-acme/lego/v3/challenge/tlsalpn01"
+ "github.com/mholt/acmez"
)
-// ConnectionPolicies is an ordered group of connection policies;
-// the first matching policy will be used to configure TLS
-// connections at handshake-time.
+// ConnectionPolicies govern the establishment of TLS connections. It is
+// an ordered group of connection policies; the first matching policy will
+// be used to configure TLS connections at handshake-time.
type ConnectionPolicies []*ConnectionPolicy
// Provision sets up each connection policy. It should be called
@@ -229,13 +229,13 @@ func (p *ConnectionPolicy) buildStandardTLSConfig(ctx caddy.Context) error {
// ensure ALPN includes the ACME TLS-ALPN protocol
var alpnFound bool
for _, a := range p.ALPN {
- if a == tlsalpn01.ACMETLS1Protocol {
+ if a == acmez.ACMETLS1Protocol {
alpnFound = true
break
}
}
if !alpnFound {
- cfg.NextProtos = append(cfg.NextProtos, tlsalpn01.ACMETLS1Protocol)
+ cfg.NextProtos = append(cfg.NextProtos, acmez.ACMETLS1Protocol)
}
// min and max protocol versions
diff --git a/modules/caddytls/dnssolver.go b/modules/caddytls/dnssolver.go
deleted file mode 100644
index c8a9c3a..0000000
--- a/modules/caddytls/dnssolver.go
+++ /dev/null
@@ -1,113 +0,0 @@
-// Copyright 2015 Matthew Holt and The Caddy Authors
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package caddytls
-
-import (
- "context"
- "fmt"
- "sync"
- "time"
-
- "github.com/go-acme/lego/v3/challenge"
- "github.com/go-acme/lego/v3/challenge/dns01"
- "github.com/libdns/libdns"
-)
-
-// TODO: this is borrowed from https://github.com/mholt/acme - once we
-// switch to that acme library, this file will go away
-
-// solver is a type that makes libdns providers usable as ACME challenge solvers.
-type solver struct {
- recordManager
-
- TTL time.Duration
-
- txtRecords map[string]libdns.Record // keyed by challenge token
- txtRecordsMu sync.Mutex
-}
-
-func (s *solver) Present(domain, token, keyAuth string) error {
- fqdn, value := dns01.GetRecord(domain, keyAuth)
-
- rec := libdns.Record{
- Type: "TXT",
- Name: fqdn,
- Value: value,
- TTL: s.TTL,
- }
-
- zone, err := dns01.FindZoneByFqdn(fqdn)
- if err != nil {
- return fmt.Errorf("could not determine zone for domain %q: %v", fqdn, err)
- }
-
- results, err := s.recordManager.AppendRecords(context.TODO(), zone, []libdns.Record{rec})
- if err != nil {
- return err
- }
- if len(results) != 1 {
- return fmt.Errorf("expected one record, got %d: %v", len(results), results)
- }
-
- // keep this record handy so we can clean it up more efficiently
- s.txtRecordsMu.Lock()
- if s.txtRecords == nil {
- s.txtRecords = make(map[string]libdns.Record)
- }
- s.txtRecords[keyAuth] = results[0]
- s.txtRecordsMu.Unlock()
-
- // TODO: check for record propagation before continuing (accordig to config)
-
- return nil
-}
-
-func (s *solver) CleanUp(domain, token, keyAuth string) error {
- fqdn, _ := dns01.GetRecord(domain, keyAuth)
- authZone, err := dns01.FindZoneByFqdn(fqdn)
- if err != nil {
- return err
- }
-
- // retrieve the record we created
- s.txtRecordsMu.Lock()
- txtRec, ok := s.txtRecords[keyAuth]
- if !ok {
- s.txtRecordsMu.Unlock()
- return fmt.Errorf("no memory of presenting a DNS record for %v", domain)
- }
- s.txtRecordsMu.Unlock()
-
- // clean up the record
- _, err = s.recordManager.DeleteRecords(context.TODO(), authZone, []libdns.Record{txtRec})
- if err != nil {
- return err
- }
-
- // once it has been successfully cleaned up, we can forget about it
- s.txtRecordsMu.Lock()
- delete(s.txtRecords, keyAuth)
- s.txtRecordsMu.Unlock()
-
- return nil
-}
-
-// recordManager defines the set of operations required for ACME challenges.
-type recordManager interface {
- libdns.RecordAppender
- libdns.RecordDeleter
-}
-
-var _ challenge.Provider = (*solver)(nil)
diff --git a/modules/caddytls/tls.go b/modules/caddytls/tls.go
index b2d05be..8178026 100644
--- a/modules/caddytls/tls.go
+++ b/modules/caddytls/tls.go
@@ -87,6 +87,7 @@ func (t *TLS) Provision(ctx caddy.Context) error {
GetConfigForCert: func(cert certmagic.Certificate) (*certmagic.Config, error) {
return t.getConfigForName(cert.Names[0]), nil
},
+ Logger: t.logger.Named("cache"),
}
if t.Automation != nil {
cacheOpts.OCSPCheckInterval = time.Duration(t.Automation.OCSPCheckInterval)
@@ -95,6 +96,9 @@ func (t *TLS) Provision(ctx caddy.Context) error {
if t.Cache != nil {
cacheOpts.Capacity = t.Cache.Capacity
}
+ if cacheOpts.Capacity <= 0 {
+ cacheOpts.Capacity = 10000
+ }
t.certCache = certmagic.NewCache(cacheOpts)
// certificate loaders
@@ -172,6 +176,7 @@ func (t *TLS) Provision(ctx caddy.Context) error {
// commands like validate can be a better test
magic := certmagic.New(t.certCache, certmagic.Config{
Storage: ctx.Storage(),
+ Logger: t.logger,
})
for _, loader := range t.certificateLoaders {
certs, err := loader.LoadCertificates()
@@ -412,13 +417,13 @@ func (t *TLS) cleanStorageUnits() {
}
// start with the default storage
- certmagic.CleanStorage(t.ctx.Storage(), options)
+ certmagic.CleanStorage(t.ctx, t.ctx.Storage(), options)
// then clean each storage defined in ACME automation policies
if t.Automation != nil {
for _, ap := range t.Automation.Policies {
if ap.storage != nil {
- certmagic.CleanStorage(ap.storage, options)
+ certmagic.CleanStorage(t.ctx, ap.storage, options)
}
}
}