From b8cba62643abf849411856bd92c42b59b98779f4 Mon Sep 17 00:00:00 2001 From: Matthew Holt Date: Fri, 6 Mar 2020 23:15:25 -0700 Subject: Refactor for CertMagic v0.10; prepare for PKI app This is a breaking change primarily in two areas: - Storage paths for certificates have changed - Slight changes to JSON config parameters Huge improvements in this commit, to be detailed more in the release notes. The upcoming PKI app will be powered by Smallstep libraries. --- modules/caddytls/acmemanager.go | 252 ---------------------------------------- 1 file changed, 252 deletions(-) delete mode 100644 modules/caddytls/acmemanager.go (limited to 'modules/caddytls/acmemanager.go') diff --git a/modules/caddytls/acmemanager.go b/modules/caddytls/acmemanager.go deleted file mode 100644 index df73545..0000000 --- a/modules/caddytls/acmemanager.go +++ /dev/null @@ -1,252 +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 ( - "crypto/x509" - "encoding/json" - "fmt" - "io/ioutil" - "net/url" - "time" - - "github.com/caddyserver/caddy/v2" - "github.com/go-acme/lego/v3/challenge" - "github.com/mholt/certmagic" -) - -func init() { - caddy.RegisterModule(ACMEManagerMaker{}) -} - -// ACMEManagerMaker makes an ACME manager -// for managing certificates using ACME. -// If crafting one manually rather than -// through the config-unmarshal process -// (provisioning), be sure to call -// SetDefaults to ensure sane defaults -// after you have configured this struct -// to your liking. -type ACMEManagerMaker struct { - // The URL to the CA's ACME directory endpoint. - CA string `json:"ca,omitempty"` - - // Your email address, so the CA can contact you if necessary. - // Not required, but strongly recommended to provide one so - // you can be reached if there is a problem. Your email is - // not sent to any Caddy mothership or used for any purpose - // other than ACME transactions. - Email string `json:"email,omitempty"` - - // How long before a certificate's expiration to try renewing it. - // Should usually be about 1/3 of certificate lifetime, but long - // enough to give yourself time to troubleshoot problems before - // expiration. Default: 30d - RenewAhead caddy.Duration `json:"renew_ahead,omitempty"` - - // The type of key to generate for the certificate. - // Supported values: `rsa2048`, `rsa4096`, `p256`, `p384`. - KeyType string `json:"key_type,omitempty"` - - // Time to wait before timing out an ACME operation. - ACMETimeout caddy.Duration `json:"acme_timeout,omitempty"` - - // If true, certificates will be requested with MustStaple. Not all - // CAs support this, and there are potentially serious consequences - // of enabling this feature without proper threat modeling. - MustStaple bool `json:"must_staple,omitempty"` - - // Configures the various ACME challenge types. - Challenges *ChallengesConfig `json:"challenges,omitempty"` - - // If true, certificates will be managed "on demand", that is, during - // TLS handshakes or when needed, as opposed to at startup or config - // load. - OnDemand bool `json:"on_demand,omitempty"` - - // Optionally configure a separate storage module associated with this - // manager, instead of using Caddy's global/default-configured storage. - Storage json.RawMessage `json:"storage,omitempty" caddy:"namespace=caddy.storage inline_key=module"` - - // An array of files of CA certificates to accept when connecting to the - // ACME CA. Generally, you should only use this if the ACME CA endpoint - // is internal or for development/testing purposes. - TrustedRootsPEMFiles []string `json:"trusted_roots_pem_files,omitempty"` - - storage certmagic.Storage - rootPool *x509.CertPool -} - -// CaddyModule returns the Caddy module information. -func (ACMEManagerMaker) CaddyModule() caddy.ModuleInfo { - return caddy.ModuleInfo{ - ID: "tls.management.acme", - New: func() caddy.Module { return new(ACMEManagerMaker) }, - } -} - -// NewManager is a no-op to satisfy the ManagerMaker interface, -// because this manager type is a special case. -func (m ACMEManagerMaker) NewManager(interactive bool) (certmagic.Manager, error) { - return nil, nil -} - -// Provision sets up m. -func (m *ACMEManagerMaker) Provision(ctx caddy.Context) error { - // DNS providers - if m.Challenges != nil && m.Challenges.DNSRaw != nil { - val, err := ctx.LoadModule(m.Challenges, "DNSRaw") - if err != nil { - return fmt.Errorf("loading DNS provider module: %v", err) - } - prov, err := val.(DNSProviderMaker).NewDNSProvider() - if err != nil { - return fmt.Errorf("making DNS provider: %v", err) - } - m.Challenges.DNS = prov - } - - // policy-specific storage implementation - if m.Storage != nil { - val, err := ctx.LoadModule(m, "Storage") - if err != nil { - return fmt.Errorf("loading TLS storage module: %v", err) - } - cmStorage, err := val.(caddy.StorageConverter).CertMagicStorage() - if err != nil { - return fmt.Errorf("creating TLS storage configuration: %v", err) - } - m.storage = cmStorage - } - - // add any custom CAs to trust store - if len(m.TrustedRootsPEMFiles) > 0 { - m.rootPool = x509.NewCertPool() - for _, pemFile := range m.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) { - return fmt.Errorf("unable to add %s to trust pool: %v", pemFile, err) - } - } - } - - return nil -} - -// makeCertMagicConfig converts m into a certmagic.Config, because -// this is a special case where the default manager is the certmagic -// Config and not a separate manager. -func (m *ACMEManagerMaker) makeCertMagicConfig(ctx caddy.Context) certmagic.Config { - storage := m.storage - if storage == nil { - storage = ctx.Storage() - } - - var ond *certmagic.OnDemandConfig - if m.OnDemand { - var onDemand *OnDemandConfig - appVal, err := ctx.App("tls") - if err == nil && appVal.(*TLS).Automation != nil { - onDemand = appVal.(*TLS).Automation.OnDemand - } - - ond = &certmagic.OnDemandConfig{ - DecisionFunc: func(name string) error { - if onDemand != nil { - if onDemand.Ask != "" { - err := onDemandAskRequest(onDemand.Ask, name) - if err != nil { - return err - } - } - // check the rate limiter last because - // doing so makes a reservation - if !onDemandRateLimiter.Allow() { - return fmt.Errorf("on-demand rate limit exceeded") - } - } - return nil - }, - } - } - - cfg := certmagic.Config{ - CA: m.CA, - Email: m.Email, - Agreed: true, - RenewDurationBefore: time.Duration(m.RenewAhead), - KeyType: supportedCertKeyTypes[m.KeyType], - CertObtainTimeout: time.Duration(m.ACMETimeout), - OnDemand: ond, - MustStaple: m.MustStaple, - Storage: storage, - TrustedRoots: m.rootPool, - // TODO: listenHost - } - - if m.Challenges != nil { - if m.Challenges.HTTP != nil { - cfg.DisableHTTPChallenge = m.Challenges.HTTP.Disabled - cfg.AltHTTPPort = m.Challenges.HTTP.AlternatePort - } - if m.Challenges.TLSALPN != nil { - cfg.DisableTLSALPNChallenge = m.Challenges.TLSALPN.Disabled - cfg.AltTLSALPNPort = m.Challenges.TLSALPN.AlternatePort - } - cfg.DNSProvider = m.Challenges.DNS - } - - return cfg -} - -// onDemandAskRequest makes a request to the ask URL -// to see if a certificate can be obtained for name. -// The certificate request should be denied if this -// returns an error. -func onDemandAskRequest(ask string, name string) error { - askURL, err := url.Parse(ask) - if err != nil { - return fmt.Errorf("parsing ask URL: %v", err) - } - qs := askURL.Query() - qs.Set("domain", name) - askURL.RawQuery = qs.Encode() - - resp, err := onDemandAskClient.Get(askURL.String()) - if err != nil { - return fmt.Errorf("error checking %v to deterine if certificate for hostname '%s' should be allowed: %v", - ask, name, err) - } - resp.Body.Close() - - if resp.StatusCode < 200 || resp.StatusCode > 299 { - return fmt.Errorf("certificate for hostname '%s' not allowed; non-2xx status code %d returned from %v", - name, resp.StatusCode, ask) - } - - return nil -} - -// DNSProviderMaker is a type that can create a new DNS provider. -// Modules in the tls.dns namespace should implement this interface. -type DNSProviderMaker interface { - NewDNSProvider() (challenge.Provider, error) -} - -// Interface guard -var _ ManagerMaker = (*ACMEManagerMaker)(nil) -- cgit v1.2.3