// ACME CLI tool package main import ( "flag" "log" "time" "dim13.org/acme" ) var ( confName = flag.String("conf", "", "configuration file") forceRenew = flag.Bool("force", false, "force renew") httpSol, tlsSol acme.Solver ) func dialProvider(p provider) error { log.Println("Dial", p.Directory) prov, err := acme.DialProvider(p.Directory) if err != nil { return err } for _, a := range p.Account { if err := loadAccount(prov, a); err != nil { return err } } return nil } func load(a account) (*acme.Account, error) { key, err := a.Load() if err != nil { return nil, err } log.Println("Load", a.KeyFile) return acme.NewAccount(key) } func register(prov *acme.Provider, a account) (*acme.Account, error) { key, err := acme.NewKey(a.KeySize) if err != nil { return nil, err } defer a.Save(key) acc, err := acme.NewAccount(key) if err != nil { return nil, err } con, err := acme.NewContacts(a.Mail, a.Phone) if err != nil { return nil, err } agree := func(tos string) bool { log.Println("agree to", tos) return true } log.Println("Register", con) if err := prov.Register(acc, con, agree); err != nil { return nil, err } return acc, nil } func loadAccount(prov *acme.Provider, a account) error { acc, err := load(a) if err != nil { acc, err = register(prov, a) if err != nil { return err } } for _, d := range a.Domain { if err := requestCert(prov, acc, d); err != nil { return err } } return nil } func requestCert(prov *acme.Provider, acc *acme.Account, d domain) error { c, err := d.Load() if err != nil { c.PrivateKey, err = acme.NewKey(d.KeySize) if err != nil { return err } } skipValid := func() bool { if c.Leaf == nil || *forceRenew { return false } return time.Now().Add(d.Gracetime).Before(c.Leaf.NotAfter) } if skipValid() { log.Println("skip valid until", c.Leaf.NotAfter) return nil } sols := acme.NewSolvers() if d.Webroot != "" { sols.Add(acme.NewWebrootSolver(d.Webroot)) } else { sols.Add(httpSol) sols.Add(tlsSol) } for _, an := range d.Altnames { log.Println("Authorize", an) if err := prov.Authorize(acc, sols, an); err != nil { return err } } log.Println("Request bundle for", d.Altnames) cert, err := prov.Bundle(acc, c.PrivateKey, d.Altnames) if err != nil { return err } log.Println("Save", d.CrtFile, d.KeyFile) if err := d.Save(cert); err != nil { return err } return nil } func main() { flag.Parse() conf, err := LoadConfig(*confName) if err != nil { log.Fatal(err) } httpSol, err = acme.NewHTTPSolver(conf.Listen) if err != nil { log.Println("HTTP Solver", err) } tlsSol, err = acme.NewTLSSolver(conf.ListenTLS) if err != nil { log.Println("TLS Solver", err) } for _, p := range conf.Provider { if err := dialProvider(p); err != nil { log.Fatal(err) } } }