// ACME CLI tool package main import ( "flag" "log" "sync" "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 } wg := sync.WaitGroup{} for _, a := range p.Account { wg.Add(1) go func(a account) { defer wg.Done() if err := loadAccount(prov, a); err != nil { log.Println(err) } }(a) } wg.Wait() return nil } func loadAccount(prov *acme.Provider, a account) error { var mustRegister bool key, err := a.Load() if err != nil { key, err = acme.NewKey(a.KeySize) if err != nil { return err } if err := a.Save(key); err != nil { return err } mustRegister = true } acc, err := acme.NewAccount(key) if err != nil { return err } if mustRegister { con, err := acme.NewContacts(a.Mail, a.Phone) if err != nil { return err } log.Println("Register", con) if err := prov.Register(acc, con); err != nil { return err } } wg := sync.WaitGroup{} for _, d := range a.Domain { wg.Add(1) go func(d domain) { defer wg.Done() if err := requestCert(prov, acc, d); err != nil { log.Println(err) } }(d) } wg.Wait() return nil } func requestCert(prov *acme.Provider, acc *acme.Account, d domain) error { c, err := d.Load() if err != nil { return err } if c.Leaf != nil && !d.renew(c.Leaf) && !*forceRenew { log.Println("skip valid until", c.Leaf.NotAfter) return nil } if c.Leaf == nil { c.PrivateKey, err = acme.NewKey(d.KeySize) if err != nil { return err } } des := acme.NewDesire(c.PrivateKey, d.Altnames) if d.Webroot != "" { des.RegisterSolver(acme.NewWebrootSolver(d.Webroot)) } else { des.RegisterSolver(httpSol) des.RegisterSolver(tlsSol) } wg := sync.WaitGroup{} for _, an := range d.Altnames { wg.Add(1) go func(an string) { defer wg.Done() log.Println("Authorize", an) if err := prov.Authorize(acc, des, an); err != nil { log.Println(err) } }(an) } wg.Wait() log.Println("Request bundle for", d.Altnames) cert, err := prov.Bundle(acc, des) 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) } } }