Let's Encrypt/ACME client and library written in Go
Find a file
2015-12-07 19:33:46 -07:00
acme Fix typo 2015-12-07 16:58:01 +01:00
.gitignore Remove global paths and default to CWD/.lego for storage. Overridable through --path. 2015-06-12 23:34:49 +02:00
.travis.yml Create .travis.yml 2015-06-13 17:36:15 +02:00
account.go Save accounts using indented JSON 2015-06-08 23:52:41 +02:00
cli.go Add a way for cronjobs to automatically renew certificates. 2015-12-06 22:35:52 +01:00
cli_handlers.go Make sure the CLI returns proper non-zero exit codes on errors. 2015-12-07 16:52:59 +01:00
configuration.go Remove CA path from config dir. 2015-09-26 20:00:19 +02:00
crypto.go Base implementation with registration support 2015-06-08 02:36:07 +02:00
LICENSE Add LICENSE 2015-06-13 12:59:39 +02:00
README.md Stub out simple CLI example 2015-12-07 19:33:46 -07:00

lego

Let's Encrypt client and ACME library written in Go

GoDoc Build Status

This is a work in progress. Please do NOT run this on a production server.

Please report any bugs you find!

Current Status

The code in this repository is under development.

Current features:

  • Registering with a CA
  • Requesting Certificates
  • Renewing Certificates
  • Revoking Certificates
  • Initiating account recovery
  • Identifier validation challenges
    • HTTP (http-01)
    • TLS with Server Name Indication (tls-sni-01)
    • Proof of Possession of a Prior Key (proofOfPossession-01)
    • DNS (dns-01) - Implemented in branch, blocked by upstream.
  • Certificate bundling
  • Library support for OCSP

Please keep in mind that CLI switches and APIs are still subject to change.

When using the standard --path option, all certificates and account configurations are saved to a folder .lego in the current working directory.

Sudo

I tried to not need sudo apart from challenges where binding to a privileged port is necessary. To run the CLI without sudo, you have two options:

  • Use setcap 'cap_net_bind_service=+ep' /path/to/program
  • Pass the --port option and specify a custom port to bind to. In this case you have to forward port 443 to this custom port.

Usage

NAME:
   lego - Let's encrypt client to go!

USAGE:
   ./lego [global options] command [command options] [arguments...]
   
VERSION:
   0.1.0
   
COMMANDS:
   run		Register an account, then create and install a certificate
   revoke	Revoke a certificate
   renew	Renew a certificate
   help, h	Shows a list of commands or help for one command
   
GLOBAL OPTIONS:
   --domains, -d [--domains option --domains option]			Add domains to the process
   --server, -s "https://acme-v01.api.letsencrypt.org/directory"	CA hostname (and optionally :port). The server certificate must be trusted in order to avoid further modifications to the client.
   --email, -m 								Email used for registration and recovery contact.
   --rsa-key-size, -B "2048"						Size of the RSA key.
   --path "${CWD}"							Directory to use for storing the data
   --port 								Challenges will use this port to listen on. Please make sure to forward port 443 to this port on your machine. Otherwise use setcap on the binary
   --help, -h								show help
   --version, -v							print the version

CLI Example

Assumes the lego binary has permission to bind to ports 80 and 443. You can get a pre-built binary from the releases page.

Obtain a certificate:

$ lego --email="foo@bar.com" --domains="example.com" run

(Find your certificate in the .lego folder of current working directory.)

To renew the certificate:

$ lego --email="foo@bar.com" --domains="example.com" renew

ACME Library Usage

A valid, but bare-bones example use of the acme package:

// You'll need a user or account type that implements acme.User
type MyUser struct {
	Email        string
	Registration *acme.RegistrationResource
	key          *rsa.PrivateKey
}
func (u MyUser) GetEmail() string {
	return u.Email
}
func (u MyUser) GetRegistration() *acme.RegistrationResource {
	return u.Registration
}
func (u MyUser) GetPrivateKey() *rsa.PrivateKey {
	return u.key
}

// Create a user. New accounts need an email and private key to start.
const rsaKeySize = 2048
privateKey, err := rsa.GenerateKey(rand.Reader, rsaKeySize)
if err != nil {
	log.Fatal(err)
}
myUser := MyUser{
	Email: "you@yours.com",
	key: privateKey,
}

// A client facilitates communication with the CA server. This CA URL is
// configured for a local dev instance of Boulder running in Docker in a VM.
// We specify an optPort of 5001 because we aren't running as root and can't
// bind a listener to port 443 (used later when we attempt to pass challenge).
client, err := acme.NewClient("http://192.168.99.100:4000", &myUser, rsaKeySize, "5001")
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, err := client.ObtainCertificates([]string{"mydomain.com"}, bundle)
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)

// ... all done.