/gravel

Integration environment for Let's Encrypt.

Primary LanguageGoMozilla Public License 2.0MPL-2.0

Documentation

Gravel

Gravel is an integration environment for Let's Encrypt.

If you're wondering why it's called Gravel, the reference ACME server is called Boulder, and it has a development server for infrastructure testing called Pebble, since it's smaller than Boulder. Well, Gravel is smaller than a Pebble.

The project came from Pebble#241 as both a tool and thought experiment on what an integration environment for Let's Encrypt would look like.

As part of that, the core of this project is a hard fork from Pebble at 7228963, so it's license is MPL-2.0. While this is a work of employees of the United States Government and our specific contributions are public domain, MPL-2.0's requirements require us to keep it licensed as MPL-2.0. For more information on this policy, see this document.

Consuming It

For the most part, consuming the integration environment is pretty straightforward.

package main

import (
	"crypto"
	"crypto/ecdsa"
	"crypto/elliptic"
	"crypto/rand"
	"fmt"
	"time"

	"github.com/18f/gravel"
	"github.com/go-acme/lego/v3/certcrypto"
	"github.com/go-acme/lego/v3/certificate"
	"github.com/go-acme/lego/v3/challenge/dns01"
	"github.com/go-acme/lego/v3/lego"
	"github.com/go-acme/lego/v3/registration"
)

type MyUser struct {
	Email        string
	Registration *registration.Resource
	key          crypto.PrivateKey
}

func (u *MyUser) GetEmail() string {
	return u.Email
}
func (u MyUser) GetRegistration() *registration.Resource {
	return u.Registration
}
func (u *MyUser) GetPrivateKey() crypto.PrivateKey {
	return u.key
}

func main() {

	opts := gravel.NewDefaultGravelOpts()
	opts.VAOpts.CustomResolverAddress = fmt.Sprintf("localhost:%d", opts.DnsOpts.DnsPort)
	g, _ := gravel.New(opts)

	// start the servers.
	go g.StartDnsServer()
	go g.StartWebServer()
	time.Sleep(3 * time.Second)

	privateKey, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)

	myuser := MyUser{
		Email: "test@test.com",
		key:   privateKey,
	}

	config := lego.NewConfig(&myuser)
	config.HTTPClient = g.Client
	config.Certificate.KeyType = certcrypto.RSA2048
	config.CADirURL = fmt.Sprintf("https://%s%s", g.Opts.ListenAddress, g.Opts.WfeOpts.DirectoryPath)

	client, _ := lego.NewClient(config)

	_ = client.Challenge.SetDNS01Provider(
		g.Opts.DnsOpts.Provider,
		dns01.AddRecursiveNameservers([]string{
			fmt.Sprintf("127.0.0.1:%d", g.Opts.DnsOpts.DnsPort),
		}),
		dns01.WrapPreCheck(g.DnsServer.PreCheck))

	reg, _ := client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true})

	myuser.Registration = reg

	request := certificate.ObtainRequest{
		Domains: []string{"test.service"},
		Bundle:  true,
	}

	certificates, _ := client.Certificate.Obtain(request)

	// do something with your valid certificate pair.
}

See the documentation on all the hooks and features available.

Developing

For the most part, it's pretty straightfoward, you can run tests with go test -v ./....