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.
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.
For the most part, it's pretty straightfoward, you can run tests with go test -v ./...
.