General cleanup and more godocs

This commit is contained in:
Matthew Holt 2019-04-26 12:35:39 -06:00
parent 2d056fbe66
commit 43961b542b
5 changed files with 129 additions and 86 deletions

View file

@ -11,15 +11,44 @@ import (
// Module represents a Caddy module. // Module represents a Caddy module.
type Module struct { type Module struct {
// Name is the full name of the module. It
// must be unique and properly namespaced.
Name string Name string
// New returns a new, empty instance of
// the module's type. The host module
// which loads this module will likely
// invoke methods on the returned value.
// It must return a pointer; if not, it
// is converted into one.
New func() (interface{}, error) New func() (interface{}, error)
// OnLoad is invoked after all module
// instances ave been loaded. It receives
// pointers to each instance of this
// module, and any state from a previous
// running configuration, which may be
// nil.
//
// If this module is to carry "global"
// state between all instances through
// reloads, you might find it helpful
// to return it.
// TODO: Is this really better/safer than a global variable?
OnLoad func(instances []interface{}, priorState interface{}) (newState interface{}, err error) OnLoad func(instances []interface{}, priorState interface{}) (newState interface{}, err error)
// OnUnload is called after all module
// instances have been stopped, possibly
// in favor of a new configuration. It
// receives the state given by OnLoad (if
// any).
OnUnload func(state interface{}) error OnUnload func(state interface{}) error
} }
func (m Module) String() string { return m.Name } func (m Module) String() string { return m.Name }
// RegisterModule registers a module. // RegisterModule registers a module. Modules must call
// this function in the init phase of runtime.
func RegisterModule(mod Module) error { func RegisterModule(mod Module) error {
if mod.Name == "caddy" { if mod.Name == "caddy" {
return fmt.Errorf("modules cannot be named 'caddy'") return fmt.Errorf("modules cannot be named 'caddy'")
@ -35,7 +64,7 @@ func RegisterModule(mod Module) error {
return nil return nil
} }
// GetModule returns a module by name. // GetModule returns a module by its full name.
func GetModule(name string) (Module, error) { func GetModule(name string) (Module, error) {
modulesMu.Lock() modulesMu.Lock()
defer modulesMu.Unlock() defer modulesMu.Unlock()

View file

@ -3,6 +3,7 @@ package caddytls
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"time"
"github.com/go-acme/lego/certcrypto" "github.com/go-acme/lego/certcrypto"
@ -18,11 +19,6 @@ func init() {
}) })
} }
// ManagerMaker TODO: WIP...
type ManagerMaker interface {
newManager(interactive bool) (certmagic.Manager, error)
}
// acmeManagerMaker makes an ACME manager // acmeManagerMaker makes an ACME manager
// for managinig certificates using ACME. // for managinig certificates using ACME.
type acmeManagerMaker struct { type acmeManagerMaker struct {
@ -40,9 +36,11 @@ type acmeManagerMaker struct {
keyType certcrypto.KeyType keyType certcrypto.KeyType
} }
func (m *acmeManagerMaker) Provision() error { func (m *acmeManagerMaker) newManager(interactive bool) (certmagic.Manager, error) {
m.setDefaults() return nil, nil
}
func (m *acmeManagerMaker) Provision() error {
// DNS providers // DNS providers
if m.Challenges.DNS != nil { if m.Challenges.DNS != nil {
val, err := caddy2.LoadModuleInline("provider", "tls.dns", m.Challenges.DNS) val, err := caddy2.LoadModuleInline("provider", "tls.dns", m.Challenges.DNS)
@ -67,18 +65,71 @@ func (m *acmeManagerMaker) Provision() error {
m.Storage = nil // allow GC to deallocate - TODO: Does this help? m.Storage = nil // allow GC to deallocate - TODO: Does this help?
} }
m.setDefaults()
return nil return nil
} }
// setDefaults indiscriminately sets all the default values in m. // setDefaults sets necessary values that are
// currently empty to their default values.
func (m *acmeManagerMaker) setDefaults() { func (m *acmeManagerMaker) setDefaults() {
if m.CA == "" {
m.CA = certmagic.LetsEncryptStagingCA // certmagic.Default.CA // TODO: When not testing, switch to production CA m.CA = certmagic.LetsEncryptStagingCA // certmagic.Default.CA // TODO: When not testing, switch to production CA
}
if m.Email == "" {
m.Email = certmagic.Default.Email m.Email = certmagic.Default.Email
}
if m.RenewAhead == 0 {
m.RenewAhead = caddy2.Duration(certmagic.Default.RenewDurationBefore) m.RenewAhead = caddy2.Duration(certmagic.Default.RenewDurationBefore)
}
if m.keyType == "" {
m.keyType = certmagic.Default.KeyType m.keyType = certmagic.Default.KeyType
}
if m.storage == nil {
m.storage = certmagic.Default.Storage m.storage = certmagic.Default.Storage
} }
}
func (m *acmeManagerMaker) newManager(interactive bool) (certmagic.Manager, error) {
return nil, 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() certmagic.Config {
storage := m.storage
if storage == nil {
storage = caddy2.GetStorage()
}
var ond *certmagic.OnDemandConfig
if m.OnDemand != nil {
ond = &certmagic.OnDemandConfig{
// TODO: fill this out
}
}
return certmagic.Config{
CA: certmagic.LetsEncryptStagingCA, //ap.CA, // TODO: Restore true value
Email: m.Email,
Agreed: true,
DisableHTTPChallenge: m.Challenges.HTTP.Disabled,
DisableTLSALPNChallenge: m.Challenges.TLSALPN.Disabled,
RenewDurationBefore: time.Duration(m.RenewAhead),
AltHTTPPort: m.Challenges.HTTP.AlternatePort,
AltTLSALPNPort: m.Challenges.TLSALPN.AlternatePort,
DNSProvider: m.Challenges.dns,
KeyType: supportedCertKeyTypes[m.KeyType],
CertObtainTimeout: time.Duration(m.ACMETimeout),
OnDemand: ond,
MustStaple: m.MustStaple,
Storage: storage,
// TODO: listenHost
}
}
// supportedCertKeyTypes is all the key types that are supported
// for certificates that are obtained through ACME.
var supportedCertKeyTypes = map[string]certcrypto.KeyType{
"RSA2048": certcrypto.RSA2048,
"RSA4096": certcrypto.RSA4096,
"P256": certcrypto.EC256,
"P384": certcrypto.EC384,
} }

View file

@ -7,12 +7,21 @@ import (
) )
type ( type (
// MatchServerName matches based on SNI.
MatchServerName []string MatchServerName []string
// TODO: these others should be enterprise-only, probably // TODO: these others should be enterprise-only, probably
MatchProtocol []string // TODO: version or protocol?
// MatchProtocol matches based on protocol.
MatchProtocol []string // TODO: Protocol or version?
// MatchClientCert matches based on client certificate / client auth?
MatchClientCert struct{} // TODO: client certificate options MatchClientCert struct{} // TODO: client certificate options
// MatchRemote matches based on the remote address of the connection.
MatchRemote []string MatchRemote []string
// MatchStarlark matches based on a Starlark script.
MatchStarlark string MatchStarlark string
) )
@ -39,6 +48,7 @@ func init() {
}) })
} }
// Match matches hello based on SNI.
func (m MatchServerName) Match(hello *tls.ClientHelloInfo) bool { func (m MatchServerName) Match(hello *tls.ClientHelloInfo) bool {
for _, name := range m { for _, name := range m {
// TODO: support wildcards (and regex?) // TODO: support wildcards (and regex?)
@ -49,21 +59,25 @@ func (m MatchServerName) Match(hello *tls.ClientHelloInfo) bool {
return false return false
} }
// Match matches hello based on protocol version.
func (m MatchProtocol) Match(hello *tls.ClientHelloInfo) bool { func (m MatchProtocol) Match(hello *tls.ClientHelloInfo) bool {
// TODO: not implemented // TODO: not implemented
return false return false
} }
// Match matches hello based on client certificate.
func (m MatchClientCert) Match(hello *tls.ClientHelloInfo) bool { func (m MatchClientCert) Match(hello *tls.ClientHelloInfo) bool {
// TODO: not implemented // TODO: not implemented
return false return false
} }
// Match matches hello based on remote address.
func (m MatchRemote) Match(hello *tls.ClientHelloInfo) bool { func (m MatchRemote) Match(hello *tls.ClientHelloInfo) bool {
// TODO: not implemented // TODO: not implemented
return false return false
} }
// Match matches hello based on a Starlark script.
func (m MatchStarlark) Match(hello *tls.ClientHelloInfo) bool { func (m MatchStarlark) Match(hello *tls.ClientHelloInfo) bool {
// TODO: not implemented // TODO: not implemented
return false return false

View file

@ -5,10 +5,8 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"net/http" "net/http"
"time"
"bitbucket.org/lightcodelabs/caddy2" "bitbucket.org/lightcodelabs/caddy2"
"github.com/go-acme/lego/certcrypto"
"github.com/go-acme/lego/challenge" "github.com/go-acme/lego/challenge"
"github.com/klauspost/cpuid" "github.com/klauspost/cpuid"
"github.com/mholt/certmagic" "github.com/mholt/certmagic"
@ -30,8 +28,7 @@ type TLS struct {
certCache *certmagic.Cache certCache *certmagic.Cache
} }
// TODO: Finish stubbing out this two-phase setup process: prepare, then start... // Provision sets up the configuration for the TLS app.
func (t *TLS) Provision() error { func (t *TLS) Provision() error {
// set up the certificate cache // set up the certificate cache
// TODO: this makes a new cache every time; better to only make a new // TODO: this makes a new cache every time; better to only make a new
@ -97,15 +94,6 @@ func (t *TLS) Start(handle caddy2.Handle) error {
if err != nil { if err != nil {
return fmt.Errorf("automate: managing %v: %v", names, err) return fmt.Errorf("automate: managing %v: %v", names, err)
} }
// for _, name := range names {
// t.Manage([]string{name)
// ap := t.getAutomationPolicyForName(name)
// magic := certmagic.New(t.certCache, ap.makeCertMagicConfig())
// err := magic.Manage([]string{name})
// if err != nil {
// return fmt.Errorf("automate: manage %s: %v", name, err)
// }
// }
} }
t.Certificates = nil // allow GC to deallocate - TODO: Does this help? t.Certificates = nil // allow GC to deallocate - TODO: Does this help?
@ -191,38 +179,11 @@ type AutomationPolicy struct {
} }
func (ap AutomationPolicy) makeCertMagicConfig() certmagic.Config { func (ap AutomationPolicy) makeCertMagicConfig() certmagic.Config {
// default manager (ACME) is a special case because of how CertMagic is designed
// TODO: refactor certmagic so that ACME manager is not a special case by extracting
// its config fields out of the certmagic.Config struct, or something...
if acmeMgmt, ok := ap.management.(*acmeManagerMaker); ok { if acmeMgmt, ok := ap.management.(*acmeManagerMaker); ok {
// default, which is management via ACME return acmeMgmt.makeCertMagicConfig()
storage := acmeMgmt.storage
if storage == nil {
storage = caddy2.GetStorage()
}
var ond *certmagic.OnDemandConfig
if acmeMgmt.OnDemand != nil {
ond = &certmagic.OnDemandConfig{
// TODO: fill this out
}
}
return certmagic.Config{
CA: certmagic.LetsEncryptStagingCA, //ap.CA, // TODO: Restore true value
Email: acmeMgmt.Email,
Agreed: true,
DisableHTTPChallenge: acmeMgmt.Challenges.HTTP.Disabled,
DisableTLSALPNChallenge: acmeMgmt.Challenges.TLSALPN.Disabled,
RenewDurationBefore: time.Duration(acmeMgmt.RenewAhead),
AltHTTPPort: acmeMgmt.Challenges.HTTP.AlternatePort,
AltTLSALPNPort: acmeMgmt.Challenges.TLSALPN.AlternatePort,
DNSProvider: acmeMgmt.Challenges.dns,
KeyType: supportedCertKeyTypes[acmeMgmt.KeyType],
CertObtainTimeout: time.Duration(acmeMgmt.ACMETimeout),
OnDemand: ond,
MustStaple: acmeMgmt.MustStaple,
Storage: storage,
// TODO: listenHost
}
} }
return certmagic.Config{ return certmagic.Config{
@ -260,13 +221,9 @@ type OnDemandConfig struct {
AskStarlark string `json:"ask_starlark,omitempty"` AskStarlark string `json:"ask_starlark,omitempty"`
} }
// supportedCertKeyTypes is all the key types that are supported // ManagerMaker makes a certificate manager.
// for certificates that are obtained through ACME. type ManagerMaker interface {
var supportedCertKeyTypes = map[string]certcrypto.KeyType{ newManager(interactive bool) (certmagic.Manager, error)
"RSA2048": certcrypto.RSA2048,
"RSA4096": certcrypto.RSA4096,
"P256": certcrypto.EC256,
"P384": certcrypto.EC384,
} }
// supportedCipherSuites is the unordered map of cipher suite // supportedCipherSuites is the unordered map of cipher suite

View file

@ -16,22 +16,14 @@ func init() {
} }
// StorageConverter is a type that can convert itself // StorageConverter is a type that can convert itself
// to a valid, usable certmagic.Storage value. The // to a valid, usable certmagic.Storage value. (The
// value might be short-lived. // value might be short-lived.) This interface allows
// us to adapt any CertMagic storage implementation
// into a consistent API for Caddy configuration.
type StorageConverter interface { type StorageConverter interface {
CertMagicStorage() (certmagic.Storage, error) CertMagicStorage() (certmagic.Storage, error)
} }
// TODO: Wrappers other than file_system should be enterprise-only.
// It may seem trivial to wrap these, but the benefits are:
// 1. We don't need to change the actual CertMagic storage implementions
// to a structure that is operable with Caddy's config (including JSON
// tags), and
// 2. We don't need to rely on rely on maintainers of third-party
// certmagic.Storage implementations. We can make any certmagic.Storage
// work with Caddy this way.
// fileStorage is a certmagic.Storage wrapper for certmagic.FileStorage. // fileStorage is a certmagic.Storage wrapper for certmagic.FileStorage.
type fileStorage struct { type fileStorage struct {
Root string `json:"root"` Root string `json:"root"`