maddy

Fork https://github.com/foxcpp/maddy

git clone git://git.lin.moe/go/maddy.git

  1package acme
  2
  3import (
  4	"context"
  5	"crypto/tls"
  6	"fmt"
  7	"path/filepath"
  8
  9	"github.com/caddyserver/certmagic"
 10	"github.com/foxcpp/maddy/framework/config"
 11	modconfig "github.com/foxcpp/maddy/framework/config/module"
 12	"github.com/foxcpp/maddy/framework/hooks"
 13	"github.com/foxcpp/maddy/framework/log"
 14	"github.com/foxcpp/maddy/framework/module"
 15)
 16
 17const modName = "tls.loader.acme"
 18
 19type Loader struct {
 20	instName string
 21
 22	store        certmagic.Storage
 23	cache        *certmagic.Cache
 24	cfg          *certmagic.Config
 25	cancelManage context.CancelFunc
 26
 27	log log.Logger
 28}
 29
 30func New(_, instName string, _, inlineArgs []string) (module.Module, error) {
 31	if len(inlineArgs) != 0 {
 32		return nil, fmt.Errorf("%s: no inline args expected", modName)
 33	}
 34	return &Loader{
 35		instName: instName,
 36		log:      log.Logger{Name: modName},
 37	}, nil
 38}
 39
 40func (l *Loader) Init(cfg *config.Map) error {
 41	var (
 42		hostname       string
 43		extraNames     []string
 44		storePath      string
 45		caPath         string
 46		testCAPath     string
 47		email          string
 48		agreed         bool
 49		challenge      string
 50		overrideDomain string
 51		provider       certmagic.DNSProvider
 52	)
 53	cfg.Bool("debug", true, false, &l.log.Debug)
 54	cfg.String("hostname", true, true, "", &hostname)
 55	cfg.StringList("extra_names", false, false, nil, &extraNames)
 56	cfg.String("store_path", false, false,
 57		filepath.Join(config.StateDirectory, "acme"), &storePath)
 58	cfg.String("ca", false, false,
 59		certmagic.LetsEncryptProductionCA, &caPath)
 60	cfg.String("test_ca", false, false,
 61		certmagic.LetsEncryptStagingCA, &testCAPath)
 62	cfg.String("email", false, false,
 63		"", &email)
 64	cfg.String("override_domain", false, false,
 65		"", &overrideDomain)
 66	cfg.Bool("agreed", false, false, &agreed)
 67	cfg.Enum("challenge", false, true,
 68		[]string{"dns-01"}, "dns-01", &challenge)
 69	cfg.Custom("dns", false, false, func() (interface{}, error) {
 70		return nil, nil
 71	}, func(m *config.Map, node config.Node) (interface{}, error) {
 72		var p certmagic.DNSProvider
 73		err := modconfig.ModuleFromNode("libdns", node.Args, node, m.Globals, &p)
 74		return p, err
 75	}, &provider)
 76	if _, err := cfg.Process(); err != nil {
 77		return err
 78	}
 79
 80	cmLog := l.log.Zap()
 81
 82	l.store = &certmagic.FileStorage{Path: storePath}
 83	l.cache = certmagic.NewCache(certmagic.CacheOptions{
 84		Logger: cmLog,
 85		GetConfigForCert: func(c certmagic.Certificate) (*certmagic.Config, error) {
 86			return l.cfg, nil
 87		},
 88	})
 89
 90	l.cfg = certmagic.New(l.cache, certmagic.Config{
 91		Storage:           l.store, // not sure if it is necessary to set these twice
 92		Logger:            cmLog,
 93		DefaultServerName: hostname,
 94	})
 95	issuer := certmagic.NewACMEIssuer(l.cfg, certmagic.ACMEIssuer{
 96		Logger: cmLog,
 97		CA:     caPath,
 98		TestCA: testCAPath,
 99		Email:  email,
100		Agreed: agreed,
101	})
102
103	switch challenge {
104	case "dns-01":
105		issuer.DisableTLSALPNChallenge = true
106		issuer.DisableHTTPChallenge = true
107		if provider == nil {
108			return fmt.Errorf("tls.loader.acme: dns-01 challenge requires a configured DNS provider")
109		}
110		issuer.DNS01Solver = &certmagic.DNS01Solver{
111			DNSManager: certmagic.DNSManager{
112				DNSProvider:    provider,
113				OverrideDomain: overrideDomain,
114			},
115		}
116	default:
117		return fmt.Errorf("tls.loader.acme: challenge not supported")
118	}
119	l.cfg.Issuers = []certmagic.Issuer{issuer}
120
121	if module.NoRun {
122		return nil
123	}
124
125	manageCtx, cancelManage := context.WithCancel(context.Background())
126	err := l.cfg.ManageAsync(manageCtx, append([]string{hostname}, extraNames...))
127	if err != nil {
128		cancelManage()
129		return err
130	}
131	l.cancelManage = cancelManage
132
133	return nil
134}
135
136func (l *Loader) ConfigureTLS(c *tls.Config) error {
137	c.GetCertificate = l.cfg.GetCertificate
138	return nil
139}
140
141func (l *Loader) Close() error {
142	l.cancelManage()
143	l.cache.Stop()
144	return nil
145}
146
147func (l *Loader) Name() string {
148	return modName
149}
150
151func (l *Loader) InstanceName() string {
152	return l.instName
153}
154
155func init() {
156	hooks.AddHook(hooks.EventShutdown, func() {
157		certmagic.CleanUpOwnLocks(context.TODO(), log.DefaultLogger.Zap())
158	})
159}
160
161func init() {
162	var _ module.TLSLoader = &Loader{}
163	module.Register(modName, New)
164}