Пример #1
0
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
}
Пример #2
0
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.")
		}
	}
}
Пример #3
0
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)
}
Пример #4
0
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
}
Пример #5
0
// 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
}
Пример #6
0
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"))
}
Пример #7
0
// 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)
	}
}
Пример #8
0
// 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
}
Пример #9
0
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
}
Пример #10
0
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
}
Пример #11
0
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
}
Пример #12
0
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!")
}
Пример #13
0
	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 == "" {
Пример #14
0
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
}
Пример #15
0
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
}
Пример #16
0
	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 == "" {
Пример #17
0
// 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
}
Пример #18
0
	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 == "" {
Пример #19
0
// 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
}
Пример #20
0
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
}
Пример #21
0
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
}
Пример #22
0
// 从 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
}
Пример #23
0
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)
		}

	}
}
Пример #24
0
	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 == "" {
Пример #25
0
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
}