func setup(c *cli.Context) (*Configuration, *Account, *acme.Client) { err := checkFolder(c.GlobalString("path")) if err != nil { logger().Fatalf("Cound not check/create path: %s", err.Error()) } conf := NewConfiguration(c) if len(c.GlobalString("email")) == 0 { logger().Fatal("You have to pass an account (email address) to the program using --email or -m") } //TODO: move to account struct? Currently MUST pass email. acc := NewAccount(c.GlobalString("email"), conf) client, err := acme.NewClient(c.GlobalString("server"), acc, conf.RsaBits()) if err != nil { logger().Fatalf("Could not create client: %s", err.Error()) } if len(c.GlobalStringSlice("exclude")) > 0 { client.ExcludeChallenges(conf.ExcludedSolvers()) } if c.GlobalIsSet("http") { client.SetHTTPAddress(c.GlobalString("http")) } if c.GlobalIsSet("tls") { client.SetTLSAddress(c.GlobalString("tls")) } return conf, acc, client }
func revoke(c *cli.Context) { err := checkFolder(c.GlobalString("path")) if err != nil { logger().Fatalf("Cound not check/create path: %v", err) } conf := NewConfiguration(c) if !c.GlobalIsSet("email") { logger().Fatal("You have to pass an account (email address) to the program using --email or -m") } acc := NewAccount(c.GlobalString("email"), conf) client := acme.NewClient(c.GlobalString("server"), acc, conf.RsaBits(), conf.OptPort()) err = checkFolder(conf.CertPath()) if err != nil { logger().Fatalf("Cound not check/create path: %v", err) } for _, domain := range c.GlobalStringSlice("domains") { logger().Printf("Trying to revoke certificate for domain %s", domain) certPath := path.Join(conf.CertPath(), domain+".crt") certBytes, err := ioutil.ReadFile(certPath) err = client.RevokeCertificate(certBytes) if err != nil { logger().Printf("Error while revoking the certificate for domain %s\n\t%v", domain, err) } else { logger().Print("Certificate was revoked.") } } }
func main() { readConf() // Create a user. New accounts need an email and private key to start. log.Printf("Generating private RSA key for user '%v'", conf.Email) const rsaKeySize = 2048 privateKey, err := rsa.GenerateKey(rand.Reader, rsaKeySize) if err != nil { log.Fatal(err) } myUser := MyUser{ Email: conf.Email, key: privateKey, } // A client facilitates communication with the CA server. Bind to PORT to // facilitate challenge requests. log.Printf("Calling ACME server: %v", conf.AcmeURI) // It's a little unfortunate that the client initializer requires a port // right now even though we won't be using one (we'll be using the DNS // challenge). Hopefully this interface will change in the near future. client, err := acme.NewClient(conf.AcmeURI, &myUser, rsaKeySize, "98234") if err != nil { log.Fatal(err) } // New users will need to register; be sure to save it reg, err := client.Register() if err != nil { log.Fatal(err) } myUser.Registration = reg // The client has a URL to the current Let's Encrypt Subscriber // Agreement. The user will need to agree to it. err = client.AgreeToTOS() if err != nil { log.Fatal(err) } // The acme library takes care of completing the challenges to obtain the certificate(s). // Of course, the hostnames must resolve to this machine or it will fail. bundle := false certificates, errs := client.ObtainCertificates([]string{conf.Domain}, bundle) for _, err := range errs { if err != nil { log.Fatal(err) } } // Each certificate comes back with the cert bytes, the bytes of the client's // private key, and a certificate URL. This is where you should save them to files! fmt.Printf("%#v\n", certificates) }
func (a *ACME) buildACMEClient(Account *Account) (*acme.Client, error) { caServer := "https://acme-v01.api.letsencrypt.org/directory" if len(a.CAServer) > 0 { caServer = a.CAServer } client, err := acme.NewClient(caServer, Account, acme.RSA4096) if err != nil { return nil, err } return client, nil }
// newClientPort does the same thing as newClient, except it creates a // new client with a custom port used for ACME transactions instead of // the default port. This is important if the default port is already in // use or is not exposed to the public, etc. func newClientPort(leEmail, port string) (*acme.Client, error) { // Look up or create the LE user account leUser, err := getUser(leEmail) if err != nil { return nil, err } // The client facilitates our communication with the CA server. client, err := acme.NewClient(CAUrl, &leUser, rsaKeySizeToUse) if err != nil { return nil, err } if port != "" { client.SetHTTPAddress(":" + port) client.SetTLSAddress(":" + port) } client.ExcludeChallenges([]acme.Challenge{acme.TLSSNI01, acme.DNS01}) // We can only guarantee http-01 at this time, but tls-01 should work if port is not custom! // If not registered, the user must register an account with the CA // and agree to terms if leUser.Registration == nil { reg, err := client.Register() if err != nil { return nil, errors.New("registration error: " + err.Error()) } leUser.Registration = reg if port == "" { // can't prompt a user who isn't there if !Agreed && reg.TosURL == "" { Agreed = promptUserAgreement(saURL, false) // TODO - latest URL } if !Agreed && reg.TosURL == "" { return nil, errors.New("user must agree to terms") } } err = client.AgreeToTOS() if err != nil { saveUser(leUser) // TODO: Might as well try, right? Error check? return nil, errors.New("error agreeing to terms: " + err.Error()) } // save user to the file system err = saveUser(leUser) if err != nil { return nil, errors.New("could not save user: " + err.Error()) } } return client, nil }
func setup(c *cli.Context) (*Configuration, *Account, *acme.Client) { err := checkFolder(c.GlobalString("path")) if err != nil { logger().Fatalf("Cound not check/create path: %v", err) } conf := NewConfiguration(c) if !c.GlobalIsSet("email") { logger().Fatal("You have to pass an account (email address) to the program using --email or -m") } //TODO: move to account struct? Currently MUST pass email. acc := NewAccount(c.GlobalString("email"), conf) return conf, acc, acme.NewClient(c.GlobalString("server"), acc, conf.RsaBits(), conf.OptPort(), c.GlobalBool("devMode")) }
// TestDNSProviderLive performs a live test to obtain a certificate // using the Let's Encrypt staging server. It runs provided that both // the environment variables GANDI_API_KEY and GANDI_TEST_DOMAIN are // set. Otherwise the test is skipped. // // To complete this test, go test must be run with the -timeout=40m // flag, since the default timeout of 10m is insufficient. func TestDNSProviderLive(t *testing.T) { apiKey := os.Getenv("GANDI_API_KEY") domain := os.Getenv("GANDI_TEST_DOMAIN") if apiKey == "" || domain == "" { t.Skip("skipping live test") } // create a user. const rsaKeySize = 2048 privateKey, err := rsa.GenerateKey(rand.Reader, rsaKeySize) if err != nil { t.Fatal(err) } myUser := user{ Email: "*****@*****.**", key: privateKey, } // create a client using staging server client, err := acme.NewClient(stagingServer, &myUser, acme.RSA2048) if err != nil { t.Fatal(err) } provider, err := NewDNSProviderCredentials(apiKey) if err != nil { t.Fatal(err) } err = client.SetChallengeProvider(acme.DNS01, provider) if err != nil { t.Fatal(err) } client.ExcludeChallenges([]acme.Challenge{acme.HTTP01, acme.TLSSNI01}) // register and agree tos reg, err := client.Register() if err != nil { t.Fatal(err) } myUser.Registration = reg err = client.AgreeToTOS() if err != nil { t.Fatal(err) } // complete the challenge bundle := false _, failures := client.ObtainCertificate([]string{domain}, bundle, nil) if len(failures) > 0 { t.Fatal(failures) } }
// newClientPort does the same thing as newClient, except it creates a // new client with a custom port used for ACME transactions instead of // the default port. This is important if the default port is already in // use or is not exposed to the public, etc. func newClientPort(leEmail, port string) (*acme.Client, error) { // Look up or create the LE user account leUser, err := getUser(leEmail) if err != nil { return nil, err } // The client facilitates our communication with the CA server. client, err := acme.NewClient(CAUrl, &leUser, rsaKeySizeToUse, port) if err != nil { return nil, err } // If not registered, the user must register an account with the CA // and agree to terms if leUser.Registration == nil { reg, err := client.Register() if err != nil { return nil, errors.New("registration error: " + err.Error()) } leUser.Registration = reg if !Agreed && reg.TosURL == "" { Agreed = promptUserAgreement(saURL, false) // TODO - latest URL } if !Agreed && reg.TosURL == "" { return nil, errors.New("user must agree to terms") } err = client.AgreeToTOS() if err != nil { saveUser(leUser) // TODO: Might as well try, right? Error check? return nil, errors.New("error agreeing to terms: " + err.Error()) } // save user to the file system err = saveUser(leUser) if err != nil { return nil, errors.New("could not save user: " + err.Error()) } } return client, nil }
func (m *Manager) verify(host string) (cert *tls.Certificate, refreshTime time.Time, err error) { c, err := acme.NewClient(letsEncryptURL, &m.state, acme.EC256) if err != nil { return } if err = c.SetChallengeProvider(acme.TLSSNI01, tlsProvider{m}); err != nil { return } c.SetChallengeProvider(acme.TLSSNI01, tlsProvider{m}) c.ExcludeChallenges([]acme.Challenge{acme.HTTP01}) acmeCert, errmap := c.ObtainCertificate([]string{host}, true, nil) if len(errmap) > 0 { if debug { log.Printf("ObtainCertificate %v => %v", host, errmap) } err = fmt.Errorf("%v", errmap) return } entryCert := stateCert{ Cert: string(acmeCert.Certificate), Key: string(acmeCert.PrivateKey), } cert, err = entryCert.toTLS() if err != nil { if debug { log.Printf("ObtainCertificate %v toTLS failure: %v", host, err) } err = err return } if refreshTime, err = certRefreshTime(cert); err != nil { return } m.mu.Lock() if m.state.Certs == nil { m.state.Certs = make(map[string]stateCert) } m.state.Certs[host] = entryCert m.mu.Unlock() m.updated() return cert, refreshTime, nil }
func (m *Manager) register(email string, prompt func(string) bool) error { if m.registered() { return fmt.Errorf("already registered") } m.state.Email = email if m.state.key == nil { key, err := newKey() if err != nil { return fmt.Errorf("generating key: %v", err) } Key, err := marshalKey(key) if err != nil { return fmt.Errorf("generating key: %v", err) } m.state.key = key m.state.Key = string(Key) } c, err := acme.NewClient(letsEncryptURL, &m.state, acme.EC256) if err != nil { return fmt.Errorf("create client: %v", err) } reg, err := c.Register() if err != nil { return fmt.Errorf("register: %v", err) } m.state.Reg = reg if reg.Body.Agreement == "" { if prompt != nil && !prompt(reg.TosURL) { return fmt.Errorf("did not agree to TOS") } if err := c.AgreeToTOS(); err != nil { return fmt.Errorf("agreeing to TOS: %v", err) } } m.updated() return nil }
func (a *ACME) buildACMEClient(account *Account) (*acme.Client, error) { log.Debugf("Building ACME client...") caServer := "https://acme-v01.api.letsencrypt.org/directory" if len(a.CAServer) > 0 { caServer = a.CAServer } client, err := acme.NewClient(caServer, account, acme.RSA4096) if err != nil { return nil, err } if len(a.DNSProvider) > 0 { log.Debugf("Using DNS Challenge provider: %s", a.DNSProvider) err = dnsOverrideDelay(a.DelayDontCheckDNS) if err != nil { return nil, err } var provider acme.ChallengeProvider provider, err = dns.NewDNSChallengeProviderByName(a.DNSProvider) if err != nil { return nil, err } client.ExcludeChallenges([]acme.Challenge{acme.HTTP01, acme.TLSSNI01}) err = client.SetChallengeProvider(acme.DNS01, provider) } else { client.ExcludeChallenges([]acme.Challenge{acme.HTTP01, acme.DNS01}) err = client.SetChallengeProvider(acme.TLSSNI01, a.challengeProvider) } if err != nil { return nil, err } return client, nil }
func main() { if flag.NArg() != 1 { flag.Usage() os.Exit(2) } if email == "" { fmt.Println("--email is required") os.Exit(2) } now := time.Now().Unix() domains := strings.Split(flag.Args()[0], ",") privateKey, err := rsa.GenerateKey(rand.Reader, rsaKeySize) if err != nil { log.Fatal(err) } if r, _ := regexp.Compile("%v"); r.MatchString(email) { email = fmt.Sprintf(email, now) } user := User{ Email: email, key: privateKey, } usersPath := fmt.Sprintf("%v/users/%v", dataPath, user.GetEmail()) log.Println(usersPath) fileWrite(usersPath, "privkey.pem", pemEncode(privateKey)) fileWrite(usersPath, "pubkey.pem", pemEncode(privateKey.Public())) // log: user log.Println(user) // A client facilitates communication with the CA server. client, err := acme.NewClient(strings.Join([]string{acmeUrl, "directory"}, "/"), &user, acme.RSA2048) if err != nil { log.Fatal(err) } // Force to use DNSimple provider, err := dnsimple.NewDNSProviderCredentials(dnsimpleEmail, dnsimpleApiKey) if err != nil { log.Fatal(err) } client.ExcludeChallenges([]acme.Challenge{acme.HTTP01, acme.TLSSNI01}) client.SetChallengeProvider(acme.DNS01, provider) if err != nil { log.Fatal(err) } // New users will need to register; be sure to save it reg, err := client.Register() if err != nil { log.Fatal(err) } user.Registration = reg // log: registration log.Println(reg) // The client has a URL to the current Let's Encrypt Subscriber Agreement. // The user will need to agree to it. err = client.AgreeToTOS() if err != nil { log.Fatal(err) } // The acme library takes care of completing the challenges to obtain the certificate(s). bundle := true certificates, failures := client.ObtainCertificate(domains, bundle, nil, false) if len(failures) > 0 { log.Fatal(failures) } // log: certificate log.Println(fmt.Printf("[INFO][%s] Certificate %s", certificates.CertURL, strings.Join(domains, ", "))) // Each certificate comes back with the cert bytes, the bytes of the client's // private key, and a certificate URL. This is where you should save them to files! //fmt.Printf("%#v\n", certificates) certsPath := fmt.Sprintf("%v/certs/%v", dataPath, now) log.Println(certsPath) fileWrite(certsPath, "privkey.pem", certificates.PrivateKey) fileWrite(certsPath, "fullchain.pem", certificates.Certificate) log.Println("completed!") }
AllowPrompts bool // if false, we assume AlternatePort must be used } // NewACMEClient creates a new ACMEClient given an email and whether // prompting the user is allowed. Clients should not be kept and // re-used over long periods of time, but immediate re-use is more // efficient than re-creating on every iteration. var NewACMEClient = func(email string, allowPrompts bool) (*ACMEClient, error) { // Look up or create the LE user account leUser, err := getUser(email) if err != nil { return nil, err } // The client facilitates our communication with the CA server. client, err := acme.NewClient(CAUrl, &leUser, KeyType) if err != nil { return nil, err } // If not registered, the user must register an account with the CA // and agree to terms if leUser.Registration == nil { reg, err := client.Register() if err != nil { return nil, errors.New("registration error: " + err.Error()) } leUser.Registration = reg if allowPrompts { // can't prompt a user who isn't there if !Agreed && reg.TosURL == "" {
func setup(c *cli.Context) (*Configuration, *Account, *acme.Client) { if c.GlobalIsSet("http-timeout") { acme.HTTPClient = http.Client{Timeout: time.Duration(c.GlobalInt("http-timeout")) * time.Second} } if c.GlobalIsSet("dns-timeout") { acme.DNSTimeout = time.Duration(c.GlobalInt("dns-timeout")) * time.Second } if len(c.GlobalStringSlice("dns-resolvers")) > 0 { resolvers := []string{} for _, resolver := range c.GlobalStringSlice("dns-resolvers") { if !strings.Contains(resolver, ":") { resolver += ":53" } resolvers = append(resolvers, resolver) } acme.RecursiveNameservers = resolvers } err := checkFolder(c.GlobalString("path")) if err != nil { logger().Fatalf("Could not check/create path: %s", err.Error()) } conf := NewConfiguration(c) if len(c.GlobalString("email")) == 0 { logger().Fatal("You have to pass an account (email address) to the program using --email or -m") } //TODO: move to account struct? Currently MUST pass email. acc := NewAccount(c.GlobalString("email"), conf) keyType, err := conf.KeyType() if err != nil { logger().Fatal(err.Error()) } client, err := acme.NewClient(c.GlobalString("server"), acc, keyType) if err != nil { logger().Fatalf("Could not create client: %s", err.Error()) } if len(c.GlobalStringSlice("exclude")) > 0 { client.ExcludeChallenges(conf.ExcludedSolvers()) } if c.GlobalIsSet("webroot") { provider, err := webroot.NewHTTPProvider(c.GlobalString("webroot")) if err != nil { logger().Fatal(err) } client.SetChallengeProvider(acme.HTTP01, provider) // --webroot=foo indicates that the user specifically want to do a HTTP challenge // infer that the user also wants to exclude all other challenges client.ExcludeChallenges([]acme.Challenge{acme.DNS01, acme.TLSSNI01}) } if c.GlobalIsSet("memcached-host") { provider, err := memcached.NewMemcachedProvider(c.GlobalStringSlice("memcached-host")) if err != nil { logger().Fatal(err) } client.SetChallengeProvider(acme.HTTP01, provider) // --memcached-host=foo:11211 indicates that the user specifically want to do a HTTP challenge // infer that the user also wants to exclude all other challenges client.ExcludeChallenges([]acme.Challenge{acme.DNS01, acme.TLSSNI01}) } if c.GlobalIsSet("http") { if strings.Index(c.GlobalString("http"), ":") == -1 { logger().Fatalf("The --http switch only accepts interface:port or :port for its argument.") } client.SetHTTPAddress(c.GlobalString("http")) } if c.GlobalIsSet("tls") { if strings.Index(c.GlobalString("tls"), ":") == -1 { logger().Fatalf("The --tls switch only accepts interface:port or :port for its argument.") } client.SetTLSAddress(c.GlobalString("tls")) } if c.GlobalIsSet("dns") { var err error var provider acme.ChallengeProvider switch c.GlobalString("dns") { case "azure": provider, err = azure.NewDNSProvider() case "auroradns": provider, err = auroradns.NewDNSProvider() case "cloudflare": provider, err = cloudflare.NewDNSProvider() case "digitalocean": provider, err = digitalocean.NewDNSProvider() case "dnsimple": provider, err = dnsimple.NewDNSProvider() case "dnsmadeeasy": provider, err = dnsmadeeasy.NewDNSProvider() case "exoscale": provider, err = exoscale.NewDNSProvider() case "dyn": provider, err = dyn.NewDNSProvider() case "gandi": provider, err = gandi.NewDNSProvider() case "gcloud": provider, err = googlecloud.NewDNSProvider() case "linode": provider, err = linode.NewDNSProvider() case "manual": provider, err = acme.NewDNSProviderManual() case "namecheap": provider, err = namecheap.NewDNSProvider() case "rackspace": provider, err = rackspace.NewDNSProvider() case "route53": provider, err = route53.NewDNSProvider() case "rfc2136": provider, err = rfc2136.NewDNSProvider() case "vultr": provider, err = vultr.NewDNSProvider() case "ovh": provider, err = ovh.NewDNSProvider() case "pdns": provider, err = pdns.NewDNSProvider() case "ns1": provider, err = ns1.NewDNSProvider() case "dnspod": provider, err = dnspod.NewDNSProvider() } if err != nil { logger().Fatal(err) } client.SetChallengeProvider(acme.DNS01, provider) // --dns=foo indicates that the user specifically want to do a DNS challenge // infer that the user also wants to exclude all other challenges client.ExcludeChallenges([]acme.Challenge{acme.HTTP01, acme.TLSSNI01}) } return conf, acc, client }
func setup(c *cli.Context) (*Configuration, *Account, *acme.Client) { err := checkFolder(c.GlobalString("path")) if err != nil { logger().Fatalf("Could not check/create path: %s", err.Error()) } conf := NewConfiguration(c) if len(c.GlobalString("email")) == 0 { logger().Fatal("You have to pass an account (email address) to the program using --email or -m") } //TODO: move to account struct? Currently MUST pass email. acc := NewAccount(c.GlobalString("email"), conf) keyType, err := conf.KeyType() if err != nil { logger().Fatal(err.Error()) } client, err := acme.NewClient(c.GlobalString("server"), acc, keyType) if err != nil { logger().Fatalf("Could not create client: %s", err.Error()) } if len(c.GlobalStringSlice("exclude")) > 0 { client.ExcludeChallenges(conf.ExcludedSolvers()) } if c.GlobalIsSet("webroot") { provider, err := webroot.NewHTTPProviderWebroot(c.GlobalString("webroot")) if err != nil { logger().Fatal(err) } client.SetChallengeProvider(acme.HTTP01, provider) // --webroot=foo indicates that the user specifically want to do a HTTP challenge // infer that the user also wants to exclude all other challenges client.ExcludeChallenges([]acme.Challenge{acme.DNS01, acme.TLSSNI01}) } if c.GlobalIsSet("http") { if strings.Index(c.GlobalString("http"), ":") == -1 { logger().Fatalf("The --http switch only accepts interface:port or :port for its argument.") } client.SetHTTPAddress(c.GlobalString("http")) } if c.GlobalIsSet("tls") { if strings.Index(c.GlobalString("tls"), ":") == -1 { logger().Fatalf("The --tls switch only accepts interface:port or :port for its argument.") } client.SetTLSAddress(c.GlobalString("tls")) } if c.GlobalIsSet("dns") { var err error var provider acme.ChallengeProvider switch c.GlobalString("dns") { case "cloudflare": provider, err = cloudflare.NewDNSProvider("", "") case "digitalocean": authToken := os.Getenv("DO_AUTH_TOKEN") provider, err = digitalocean.NewDNSProvider(authToken) case "dnsimple": provider, err = dnsimple.NewDNSProvider("", "") case "route53": awsRegion := os.Getenv("AWS_REGION") provider, err = route53.NewDNSProvider("", "", awsRegion) case "rfc2136": nameserver := os.Getenv("RFC2136_NAMESERVER") tsigAlgorithm := os.Getenv("RFC2136_TSIG_ALGORITHM") tsigKey := os.Getenv("RFC2136_TSIG_KEY") tsigSecret := os.Getenv("RFC2136_TSIG_SECRET") provider, err = rfc2136.NewDNSProvider(nameserver, tsigAlgorithm, tsigKey, tsigSecret) case "manual": provider, err = acme.NewDNSProviderManual() } if err != nil { logger().Fatal(err) } client.SetChallengeProvider(acme.DNS01, provider) // --dns=foo indicates that the user specifically want to do a DNS challenge // infer that the user also wants to exclude all other challenges client.ExcludeChallenges([]acme.Challenge{acme.HTTP01, acme.TLSSNI01}) } return conf, acc, client }
AllowPrompts bool // if false, we assume AlternatePort must be used } // NewACMEClient creates a new ACMEClient given an email and whether // prompting the user is allowed. Clients should not be kept and // re-used over long periods of time, but immediate re-use is more // efficient than re-creating on every iteration. var NewACMEClient = func(email string, allowPrompts bool) (*ACMEClient, error) { // Look up or create the LE user account leUser, err := getUser(email) if err != nil { return nil, err } // The client facilitates our communication with the CA server. client, err := acme.NewClient(CAUrl, &leUser, rsaKeySizeToUse) if err != nil { return nil, err } // If not registered, the user must register an account with the CA // and agree to terms if leUser.Registration == nil { reg, err := client.Register() if err != nil { return nil, errors.New("registration error: " + err.Error()) } leUser.Registration = reg if allowPrompts { // can't prompt a user who isn't there if !Agreed && reg.TosURL == "" {
// NewClient returns a new Lets Encrypt client func NewClient(email string, kt KeyType, apiVer ApiVersion, provider ProviderOpts) (*Client, error) { var keyType lego.KeyType switch kt { case RSA2048: keyType = lego.RSA2048 case RSA4096: keyType = lego.RSA4096 case RSA8192: keyType = lego.RSA8192 case EC256: keyType = lego.EC256 case EC384: keyType = lego.EC384 default: return nil, fmt.Errorf("Invalid private key type: %s", string(kt)) } var serverUri string switch apiVer { case Production: serverUri = ProductionApiUri case Sandbox: serverUri = StagingApiUri default: return nil, fmt.Errorf("Invalid API version: %s", string(apiVer)) } acc, err := NewAccount(email, apiVer, keyType) if err != nil { return nil, fmt.Errorf("Could not initialize account store for %s: %v", email, err) } client, err := lego.NewClient(serverUri, acc, keyType) if err != nil { return nil, fmt.Errorf("Could not create client: %v", err) } lego.Logger = log.New(ioutil.Discard, "", 0) if acc.Registration == nil { logrus.Infof("Creating Let's Encrypt account for %s", email) reg, err := client.Register() if err != nil { return nil, fmt.Errorf("Failed to register account: %v", err) } acc.Registration = reg if acc.Registration.Body.Agreement == "" { err = client.AgreeToTOS() if err != nil { return nil, fmt.Errorf("Could not agree to TOS: %v", err) } } err = acc.Save() if err != nil { logrus.Errorf("Could not save account data: %v", err) } } else { logrus.Infof("Using locally stored Let's Encrypt account for %s", email) } prov, err := getProvider(provider) if err != nil { return nil, fmt.Errorf("Could not set DNS provider: %v", err) } err = client.SetChallengeProvider(lego.DNS01, prov) if err != nil { return nil, fmt.Errorf("Could not set DNS provider: %v", err) } client.ExcludeChallenges([]lego.Challenge{lego.HTTP01, lego.TLSSNI01}) return &Client{ client: client, apiVersion: apiVer, }, nil }
leUser, err := getUser(email) if err != nil { return nil, err } // The client facilitates our communication with the CA server. var kt acme.KeyType if rsaKeySizeToUse == Rsa2048 { kt = acme.RSA2048 } else if rsaKeySizeToUse == Rsa4096 { kt = acme.RSA4096 } else { // TODO(hkjn): Support more types? Current changes are quick fix for #640. return nil, fmt.Errorf("https: unsupported keysize") } client, err := acme.NewClient(CAUrl, &leUser, kt) if err != nil { return nil, err } // If not registered, the user must register an account with the CA // and agree to terms if leUser.Registration == nil { reg, err := client.Register() if err != nil { return nil, errors.New("registration error: " + err.Error()) } leUser.Registration = reg if allowPrompts { // can't prompt a user who isn't there if !Agreed && reg.TosURL == "" {
// Start launches this services. func (s *acmeService) Start() error { // Check arguments missingArgs := []string{} if s.Email == "" { missingArgs = append(missingArgs, "acme-email") } if s.CADirectoryURL == "" { missingArgs = append(missingArgs, "acme-directory-url") } if s.PrivateKeyPath == "" { missingArgs = append(missingArgs, "private-key-path") } if s.RegistrationPath == "" { missingArgs = append(missingArgs, "registration-path") } if len(missingArgs) > 0 { s.Logger.Warningf("ACME is not configured, some it will not be used. Missing: %v", missingArgs) return nil } // Load private key key, err := s.getPrivateKey() if err != nil { return maskAny(err) } // Load registration registration, err := s.getRegistration() if err != nil { return maskAny(err) } if registration == nil { return maskAny(fmt.Errorf("No registration found at %s", s.RegistrationPath)) } // Create ACME client user := acmeUser{ Email: s.Email, Registration: registration, PrivateKey: key, } client, err := acme.NewClient(s.CADirectoryURL, user, s.KeyBits) if err != nil { return maskAny(err) } client.ExcludeChallenges([]acme.Challenge{acme.TLSSNI01, acme.DNS01}) client.SetChallengeProvider(acme.HTTP01, newHttpChallengeProvider(s.HttpProviderConfig, s.HttpProviderDependencies)) // Save objects s.Requester.Initialize(client) // Start HTTP challenge listener if err := s.httpProvider.Start(); err != nil { return maskAny(err) } // Monitor the repository for changes s.repositoryMonitorLoop() // Start the renewal monitor s.Renewal.Start() // We're now active s.active = true return nil }
func setup(c *cli.Context) (*Configuration, *Account, *acme.Client) { err := checkFolder(c.GlobalString("path")) if err != nil { logger().Fatalf("Cound not check/create path: %s", err.Error()) } conf := NewConfiguration(c) if len(c.GlobalString("email")) == 0 { logger().Fatal("You have to pass an account (email address) to the program using --email or -m") } //TODO: move to account struct? Currently MUST pass email. acc := NewAccount(c.GlobalString("email"), conf) client, err := acme.NewClient(c.GlobalString("server"), acc, conf.RsaBits()) if err != nil { logger().Fatalf("Could not create client: %s", err.Error()) } if len(c.GlobalStringSlice("exclude")) > 0 { client.ExcludeChallenges(conf.ExcludedSolvers()) } if c.GlobalIsSet("http") { client.SetHTTPAddress(c.GlobalString("http")) } if c.GlobalIsSet("tls") { client.SetTLSAddress(c.GlobalString("tls")) } if c.GlobalIsSet("dns") { var err error var provider acme.ChallengeProvider switch c.GlobalString("dns") { case "cloudflare": provider, err = acme.NewDNSProviderCloudFlare("", "") case "digitalocean": authToken := os.Getenv("DO_AUTH_TOKEN") provider, err = acme.NewDNSProviderDigitalOcean(authToken) case "dnsimple": provider, err = acme.NewDNSProviderDNSimple("", "") case "route53": awsRegion := os.Getenv("AWS_REGION") provider, err = acme.NewDNSProviderRoute53("", "", awsRegion) case "rfc2136": nameserver := os.Getenv("RFC2136_NAMESERVER") zone := os.Getenv("RFC2136_ZONE") tsigKey := os.Getenv("RFC2136_TSIG_KEY") tsigSecret := os.Getenv("RFC2136_TSIG_SECRET") provider, err = acme.NewDNSProviderRFC2136(nameserver, zone, tsigKey, tsigSecret) case "manual": provider, err = acme.NewDNSProviderManual() } if err != nil { logger().Fatal(err) } client.SetChallengeProvider(acme.DNS01, provider) } return conf, acc, client }
func setup(c *cli.Context) (*Configuration, *Account, *acme.Client) { err := checkFolder(c.GlobalString("path")) if err != nil { logger().Fatalf("Could not check/create path: %s", err.Error()) } conf := NewConfiguration(c) if len(c.GlobalString("email")) == 0 { logger().Fatal("You have to pass an account (email address) to the program using --email or -m") } //TODO: move to account struct? Currently MUST pass email. acc := NewAccount(c.GlobalString("email"), conf) keyType, err := conf.KeyType() if err != nil { logger().Fatal(err.Error()) } client, err := acme.NewClient(c.GlobalString("server"), acc, keyType) if err != nil { logger().Fatalf("Could not create client: %s", err.Error()) } if len(c.GlobalStringSlice("exclude")) > 0 { client.ExcludeChallenges(conf.ExcludedSolvers()) } if c.GlobalIsSet("webroot") { provider, err := webroot.NewHTTPProvider(c.GlobalString("webroot")) if err != nil { logger().Fatal(err) } client.SetChallengeProvider(acme.HTTP01, provider) // --webroot=foo indicates that the user specifically want to do a HTTP challenge // infer that the user also wants to exclude all other challenges client.ExcludeChallenges([]acme.Challenge{acme.DNS01, acme.TLSSNI01}) } if c.GlobalIsSet("http") { if strings.Index(c.GlobalString("http"), ":") == -1 { logger().Fatalf("The --http switch only accepts interface:port or :port for its argument.") } client.SetHTTPAddress(c.GlobalString("http")) } if c.GlobalIsSet("tls") { if strings.Index(c.GlobalString("tls"), ":") == -1 { logger().Fatalf("The --tls switch only accepts interface:port or :port for its argument.") } client.SetTLSAddress(c.GlobalString("tls")) } if c.GlobalIsSet("dns") { var err error var provider acme.ChallengeProvider switch c.GlobalString("dns") { case "cloudflare": provider, err = cloudflare.NewDNSProvider() case "digitalocean": provider, err = digitalocean.NewDNSProvider() case "dnsimple": provider, err = dnsimple.NewDNSProvider() case "dyn": provider, err = dyn.NewDNSProvider() case "gandi": provider, err = gandi.NewDNSProvider() case "gcloud": provider, err = googlecloud.NewDNSProvider() case "manual": provider, err = acme.NewDNSProviderManual() case "namecheap": provider, err = namecheap.NewDNSProvider() case "route53": provider, err = route53.NewDNSProvider() case "rfc2136": provider, err = rfc2136.NewDNSProvider() case "vultr": provider, err = vultr.NewDNSProvider() } if err != nil { logger().Fatal(err) } client.SetChallengeProvider(acme.DNS01, provider) // --dns=foo indicates that the user specifically want to do a DNS challenge // infer that the user also wants to exclude all other challenges client.ExcludeChallenges([]acme.Challenge{acme.HTTP01, acme.TLSSNI01}) } return conf, acc, client }
// 从 Let's Encrypt 得到证书 func getCertificate(domain string, email string, certificateFile string, keyFile string) error { // 生成认证用户 const rsaKeySize = 2048 privateKey, err := rsa.GenerateKey(rand.Reader, rsaKeySize) if err != nil { log.Fatal(err) } myUser := MyUser{ Email: email, key: privateKey, } // 建立客户端 client, err := acme.NewClient(letsencryptEndpoint, &myUser, acme.RSA2048) if err != nil { return err } // 注册用户 reg, err := client.Register() if err != nil { return err } myUser.Registration = reg // 同意 Let's Encrypt 的服务协议 err = client.AgreeToTOS() if err != nil { return err } // 获取认证,本机必须能够访问认证域名 bundle := false certificates, failures := client.ObtainCertificate([]string{domain}, bundle, nil) if len(failures) > 0 { return errors.New("无法得到证书") } // 读入 Let's Encrypt 的证书 intermediate, err := ioutil.ReadFile("intermediate.pem") if err != nil { return errors.New("无法读入 intermediate pem") } // 生成域名认证文件 if err := ioutil.WriteFile( certificateFile, bytes.Join([][]byte{certificates.Certificate, intermediate}, nil), 0600); err != nil { return err } // 生成域名认证秘钥 if certificates.PrivateKey != nil { err = ioutil.WriteFile(keyFile, certificates.PrivateKey, 0600) if err != nil { return err } } return nil }
func run(c *cli.Context) { err := checkFolder(c.GlobalString("path")) if err != nil { logger().Fatalf("Cound not check/create path: %v", err) } conf := NewConfiguration(c) //TODO: move to account struct? Currently MUST pass email. if !c.GlobalIsSet("email") { logger().Fatal("You have to pass an account (email address) to the program using --email or -m") } acc := NewAccount(c.GlobalString("email"), conf) client := acme.NewClient(c.GlobalString("server"), acc, conf.RsaBits(), conf.OptPort()) if acc.Registration == nil { reg, err := client.Register() if err != nil { logger().Fatalf("Could not complete registration -> %v", err) } acc.Registration = reg acc.Save() logger().Print("!!!! HEADS UP !!!!") logger().Printf(` Your account credentials have been saved in your Let's Encrypt configuration directory at "%s". You should make a secure backup of this folder now. This configuration directory will also contain certificates and private keys obtained from Let's Encrypt so making regular backups of this folder is ideal. If you lose your account credentials, you can recover them using the token "%s". You must write that down and put it in a safe place.`, c.GlobalString("config-dir"), reg.Body.Recoverytoken) } if acc.Registration.Body.Agreement == "" { if !c.GlobalBool("agree-tos") { reader := bufio.NewReader(os.Stdin) logger().Printf("Please review the TOS at %s", acc.Registration.TosURL) for { logger().Println("Do you accept the TOS? Y/n") text, err := reader.ReadString('\n') if err != nil { logger().Fatalf("Could not read from console -> %v", err) } text = strings.Trim(text, "\r\n") if text == "n" { logger().Fatal("You did not accept the TOS. Unable to proceed.") } if text == "Y" || text == "y" || text == "" { err = client.AgreeToTos() if err != nil { logger().Fatalf("Could not agree to tos -> %v", err) } acc.Save() break } logger().Println("Your input was invalid. Please answer with one of Y/y, n or by pressing enter.") } } } if !c.GlobalIsSet("domains") { logger().Fatal("Please specify --domains") } certs, err := client.ObtainCertificates(c.GlobalStringSlice("domains")) if err != nil { logger().Fatalf("Could not obtain certificates -> %v", err) } err = checkFolder(conf.CertPath()) if err != nil { logger().Fatalf("Cound not check/create path: %v", err) } for _, certRes := range certs { certOut := path.Join(conf.CertPath(), certRes.Domain+".crt") privOut := path.Join(conf.CertPath(), certRes.Domain+".key") err = ioutil.WriteFile(certOut, certRes.Certificate, 0700) if err != nil { logger().Printf("Unable to save Certificate for domain %s -> %v", certRes.Domain, err) } err = ioutil.WriteFile(privOut, certRes.PrivateKey, 0700) if err != nil { logger().Printf("Unable to save PrivateKey for domain %s -> %v", certRes.Domain, err) } } }
caURL := DefaultCAUrl if config.CAUrl != "" { caURL = config.CAUrl } // ensure endpoint is secure (assume HTTPS if scheme is missing) if !strings.Contains(caURL, "://") { caURL = "https://" + caURL } u, err := url.Parse(caURL) if u.Scheme != "https" && !caddy.IsLoopback(u.Host) && !strings.HasPrefix(u.Host, "10.") { return nil, fmt.Errorf("%s: insecure CA URL (HTTPS required)", caURL) } // The client facilitates our communication with the CA server. client, err := acme.NewClient(caURL, &leUser, keyType) if err != nil { return nil, err } // If not registered, the user must register an account with the CA // and agree to terms if leUser.Registration == nil { reg, err := client.Register() if err != nil { return nil, errors.New("registration error: " + err.Error()) } leUser.Registration = reg if allowPrompts { // can't prompt a user who isn't there if !Agreed && reg.TosURL == "" {
func setup(c *cli.Context) (*Configuration, *Account, *acme.Client) { if c.GlobalIsSet("http-timeout") { acme.HTTPClient = http.Client{Timeout: time.Duration(c.GlobalInt("http-timeout")) * time.Second} } if c.GlobalIsSet("dns-timeout") { acme.DNSTimeout = time.Duration(c.GlobalInt("dns-timeout")) * time.Second } if len(c.GlobalStringSlice("dns-resolvers")) > 0 { resolvers := []string{} for _, resolver := range c.GlobalStringSlice("dns-resolvers") { if !strings.Contains(resolver, ":") { resolver += ":53" } resolvers = append(resolvers, resolver) } acme.RecursiveNameservers = resolvers } err := checkFolder(c.GlobalString("path")) if err != nil { logger().Fatalf("Could not check/create path: %s", err.Error()) } conf := NewConfiguration(c) if len(c.GlobalString("email")) == 0 { logger().Fatal("You have to pass an account (email address) to the program using --email or -m") } //TODO: move to account struct? Currently MUST pass email. acc := NewAccount(c.GlobalString("email"), conf) keyType, err := conf.KeyType() if err != nil { logger().Fatal(err.Error()) } client, err := acme.NewClient(c.GlobalString("server"), acc, keyType) if err != nil { logger().Fatalf("Could not create client: %s", err.Error()) } if len(c.GlobalStringSlice("exclude")) > 0 { client.ExcludeChallenges(conf.ExcludedSolvers()) } if c.GlobalIsSet("webroot") { provider, err := webroot.NewHTTPProvider(c.GlobalString("webroot")) if err != nil { logger().Fatal(err) } client.SetChallengeProvider(acme.HTTP01, provider) // --webroot=foo indicates that the user specifically want to do a HTTP challenge // infer that the user also wants to exclude all other challenges client.ExcludeChallenges([]acme.Challenge{acme.DNS01, acme.TLSSNI01}) } if c.GlobalIsSet("memcached-host") { provider, err := memcached.NewMemcachedProvider(c.GlobalStringSlice("memcached-host")) if err != nil { logger().Fatal(err) } client.SetChallengeProvider(acme.HTTP01, provider) // --memcached-host=foo:11211 indicates that the user specifically want to do a HTTP challenge // infer that the user also wants to exclude all other challenges client.ExcludeChallenges([]acme.Challenge{acme.DNS01, acme.TLSSNI01}) } if c.GlobalIsSet("http") { if strings.Index(c.GlobalString("http"), ":") == -1 { logger().Fatalf("The --http switch only accepts interface:port or :port for its argument.") } client.SetHTTPAddress(c.GlobalString("http")) } if c.GlobalIsSet("tls") { if strings.Index(c.GlobalString("tls"), ":") == -1 { logger().Fatalf("The --tls switch only accepts interface:port or :port for its argument.") } client.SetTLSAddress(c.GlobalString("tls")) } if c.GlobalIsSet("dns") { provider, err := dns.NewDNSChallengeProviderByName(c.GlobalString("dns")) if err != nil { logger().Fatal(err) } client.SetChallengeProvider(acme.DNS01, provider) // --dns=foo indicates that the user specifically want to do a DNS challenge // infer that the user also wants to exclude all other challenges client.ExcludeChallenges([]acme.Challenge{acme.HTTP01, acme.TLSSNI01}) } return conf, acc, client }