E2E tests for certmagic ACME provider

* Actually set the CA
* Fix the certmangic.storage interface to return the correct error type
* Write an e2e test for certmagic against the let's encrypt staging CA
This commit is contained in:
Jake Sanders 2019-10-17 16:31:02 +01:00
parent 9d559848c2
commit 4885bba2ac
4 changed files with 71 additions and 1 deletions

View File

@ -3,7 +3,9 @@ package certmagic
import ( import (
"log" "log"
"math/rand"
"net" "net"
"time"
"github.com/mholt/certmagic" "github.com/mholt/certmagic"
@ -15,6 +17,7 @@ type certmagicProvider struct {
} }
func (c *certmagicProvider) NewListener(ACMEHosts ...string) (net.Listener, error) { func (c *certmagicProvider) NewListener(ACMEHosts ...string) (net.Listener, error) {
certmagic.Default.CA = c.opts.CA
if c.opts.ChallengeProvider != nil { if c.opts.ChallengeProvider != nil {
// Enabling DNS Challenge disables the other challenges // Enabling DNS Challenge disables the other challenges
certmagic.Default.DNSProvider = c.opts.ChallengeProvider certmagic.Default.DNSProvider = c.opts.ChallengeProvider
@ -22,6 +25,16 @@ func (c *certmagicProvider) NewListener(ACMEHosts ...string) (net.Listener, erro
if c.opts.OnDemand { if c.opts.OnDemand {
certmagic.Default.OnDemand = new(certmagic.OnDemandConfig) certmagic.Default.OnDemand = new(certmagic.OnDemandConfig)
} }
if c.opts.Cache != nil {
// already validated by new()
certmagic.Default.Storage = c.opts.Cache.(certmagic.Storage)
}
// If multiple instances of the provider are running, inject some
// randomness so they don't collide
rand.Seed(time.Now().UnixNano())
randomDuration := (7 * 24 * time.Hour) + (time.Duration(rand.Intn(504)) * time.Hour)
certmagic.Default.RenewDurationBefore = randomDuration
return certmagic.Listen(ACMEHosts) return certmagic.Listen(ACMEHosts)
} }

View File

@ -1,6 +1,7 @@
package certmagic package certmagic
import ( import (
"net/http"
"os" "os"
"reflect" "reflect"
"sort" "sort"
@ -185,3 +186,46 @@ func TestStorageImplementation(t *testing.T) {
// happens // happens
New(acme.Cache(s)) New(acme.Cache(s))
} }
// Full test with a real zone, with against LE staging
func TestE2e(t *testing.T) {
apiToken, accountID := os.Getenv("CF_API_TOKEN"), os.Getenv("CF_ACCOUNT_ID")
kvID := os.Getenv("KV_NAMESPACE_ID")
if len(apiToken) == 0 || len(accountID) == 0 || len(kvID) == 0 {
t.Skip("No Cloudflare API keys available, skipping test")
}
testLock := memory.NewLock()
testStore, err := cloudflarestorage.New(
options.WithValue("CF_API_TOKEN", apiToken),
options.WithValue("CF_ACCOUNT_ID", accountID),
options.WithValue("KV_NAMESPACE_ID", kvID),
)
if err != nil {
t.Fatal(err.Error())
}
testStorage := NewStorage(testLock, testStore)
conf := cloudflare.NewDefaultConfig()
conf.AuthToken = apiToken
conf.ZoneToken = apiToken
testChallengeProvider, err := cloudflare.NewDNSProviderConfig(conf)
if err != nil {
t.Fatal(err.Error())
}
testProvider := New(
acme.AcceptToS(true),
acme.Cache(testStorage),
acme.CA(acme.LetsEncryptStagingCA),
acme.ChallengeProvider(testChallengeProvider),
acme.OnDemand(false),
)
listener, err := testProvider.NewListener("*.micro.mu", "micro.mu")
if err != nil {
t.Fatal(err.Error())
}
go http.Serve(listener, http.NotFoundHandler())
time.Sleep(10 * time.Minute)
}

View File

@ -3,6 +3,7 @@ package certmagic
import ( import (
"bytes" "bytes"
"encoding/gob" "encoding/gob"
"errors"
"fmt" "fmt"
"path" "path"
"strings" "strings"
@ -55,6 +56,9 @@ func (s *storage) Store(key string, value []byte) error {
} }
func (s *storage) Load(key string) ([]byte, error) { func (s *storage) Load(key string) ([]byte, error) {
if !s.Exists(key) {
return nil, certmagic.ErrNotExist(errors.New(key + " doesn't exist"))
}
records, err := s.store.Read(key) records, err := s.store.Read(key)
if err != nil { if err != nil {
return nil, err return nil, err
@ -77,7 +81,7 @@ func (s *storage) Delete(key string) error {
} }
func (s *storage) Exists(key string) bool { func (s *storage) Exists(key string) bool {
_, err := s.store.Read() _, err := s.store.Read(key)
if err != nil { if err != nil {
return false return false
} }
@ -132,3 +136,11 @@ func (s *storage) Stat(key string) (certmagic.KeyInfo, error) {
IsTerminal: false, IsTerminal: false,
}, nil }, nil
} }
// NewStorage returns a certmagic.Storage backed by a go-micro/lock and go-micro/store
func NewStorage(lock lock.Lock, store store.Store) certmagic.Storage {
return &storage{
lock: lock,
store: store,
}
}

1
go.mod
View File

@ -36,6 +36,7 @@ require (
github.com/jonboulle/clockwork v0.1.0 // indirect github.com/jonboulle/clockwork v0.1.0 // indirect
github.com/joncalhoun/qson v0.0.0-20170526102502-8a9cab3a62b1 github.com/joncalhoun/qson v0.0.0-20170526102502-8a9cab3a62b1
github.com/json-iterator/go v1.1.7 github.com/json-iterator/go v1.1.7
github.com/kr/pretty v0.1.0
github.com/leodido/go-urn v1.1.0 // indirect github.com/leodido/go-urn v1.1.0 // indirect
github.com/lucas-clemente/quic-go v0.12.1 github.com/lucas-clemente/quic-go v0.12.1
github.com/mholt/certmagic v0.7.5 github.com/mholt/certmagic v0.7.5