mirror of
https://github.com/pomerium/pomerium.git
synced 2025-04-30 02:46:30 +02:00
The `autocert_ca` and `autocert_email` options have been added to be able to configure CAs that support the ACME protocol as an alternative to Let's Encrypt. Fix ProtoBuf definition for additional autocert options Fix PR comments and add ACME EAB configuration Add configuration option for trusted CAs when talking ACME Fix linter issues copy edits render updated reference to docs Add test for autocert manager configuration Add tests for autocert configuration options Fix CI build issues Don't set empty acme.EAB struct if configuration not set Remove required email when setting custom CA When using a non-default CA it's no longer required to specify an email address. I required this before, because it seemed to cause an issue in which no certificate was issued. The root cause was something different, rendering the hard email requirement pointless. It's still beneficial to specify an email, though. I changed the text in the docs to explain that. Update generated docs Fix failing tests by recreation of a new ACMEManager The default ACMEManager object was reused in multiple tests, resulting in unexpected states when tests run in parallel. By using a new instance for every test, this is no longer an issue.
629 lines
17 KiB
Go
629 lines
17 KiB
Go
package autocert
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"crypto/ecdsa"
|
|
"crypto/elliptic"
|
|
"crypto/rand"
|
|
"crypto/x509"
|
|
"crypto/x509/pkix"
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"encoding/pem"
|
|
"fmt"
|
|
"io"
|
|
"io/ioutil"
|
|
"math/big"
|
|
"net"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/caddyserver/certmagic"
|
|
"github.com/go-chi/chi"
|
|
"github.com/go-chi/chi/middleware"
|
|
"github.com/google/go-cmp/cmp"
|
|
"github.com/google/go-cmp/cmp/cmpopts"
|
|
"github.com/google/uuid"
|
|
"github.com/mholt/acmez/acme"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"golang.org/x/crypto/ocsp"
|
|
|
|
"github.com/pomerium/pomerium/config"
|
|
"github.com/pomerium/pomerium/internal/log"
|
|
)
|
|
|
|
type M = map[string]interface{}
|
|
|
|
type testCA struct {
|
|
key *ecdsa.PrivateKey
|
|
cert *x509.Certificate
|
|
certPEM []byte
|
|
}
|
|
|
|
func newTestCA() (*testCA, error) {
|
|
key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
tpl := &x509.Certificate{
|
|
SerialNumber: big.NewInt(time.Now().Unix()),
|
|
Subject: pkix.Name{
|
|
CommonName: "Test CA",
|
|
},
|
|
NotBefore: time.Now(),
|
|
NotAfter: time.Now().Add(time.Minute * 10),
|
|
|
|
KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageDigitalSignature,
|
|
BasicConstraintsValid: true,
|
|
IsCA: true,
|
|
}
|
|
|
|
der, err := x509.CreateCertificate(rand.Reader, tpl, tpl, &key.PublicKey, key)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
cert, err := x509.ParseCertificate(der)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &testCA{
|
|
key,
|
|
cert,
|
|
pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: der}),
|
|
}, nil
|
|
}
|
|
|
|
func newMockACME(ca *testCA, srv *httptest.Server) http.Handler {
|
|
var certBuffer bytes.Buffer
|
|
|
|
var certs []*x509.Certificate
|
|
findCert := func(serial *big.Int) *x509.Certificate {
|
|
for _, c := range certs {
|
|
if c.SerialNumber.Cmp(serial) == 0 {
|
|
return c
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
r := chi.NewRouter()
|
|
r.Use(middleware.Logger)
|
|
r.Get("/acme/directory", func(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
_ = json.NewEncoder(w).Encode(M{
|
|
"keyChange": srv.URL + "/acme/key-change",
|
|
"newAccount": srv.URL + "/acme/new-acct",
|
|
"newNonce": srv.URL + "/acme/new-nonce",
|
|
"newOrder": srv.URL + "/acme/new-order",
|
|
"revokeCert": srv.URL + "/acme/revoke-cert",
|
|
})
|
|
})
|
|
r.Head("/acme/new-nonce", func(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Set("Replay-Nonce", "NONCE")
|
|
w.WriteHeader(http.StatusOK)
|
|
})
|
|
r.Post("/acme/new-acct", func(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Set("Replay-Nonce", "NONCE")
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.WriteHeader(http.StatusCreated)
|
|
_ = json.NewEncoder(w).Encode(M{
|
|
"status": "valid",
|
|
})
|
|
})
|
|
r.Post("/acme/new-order", func(w http.ResponseWriter, r *http.Request) {
|
|
var payload struct {
|
|
Identifiers []struct {
|
|
Type string `json:"type"`
|
|
Value string `json:"value"`
|
|
} `json:"identifiers"`
|
|
}
|
|
readJWSPayload(r.Body, &payload)
|
|
w.Header().Set("Replay-Nonce", "NONCE")
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.WriteHeader(http.StatusCreated)
|
|
_ = json.NewEncoder(w).Encode(M{
|
|
"status": "pending",
|
|
"finalize": srv.URL + "/acme/finalize",
|
|
})
|
|
})
|
|
r.Post("/ocsp/request", func(w http.ResponseWriter, r *http.Request) {
|
|
reqData, _ := io.ReadAll(r.Body)
|
|
ocspReq, _ := ocsp.ParseRequest(reqData)
|
|
ocspResp := ocsp.Response{
|
|
Status: ocsp.Good,
|
|
SerialNumber: ocspReq.SerialNumber,
|
|
ThisUpdate: time.Now(),
|
|
NextUpdate: time.Now().Add(time.Second),
|
|
}
|
|
|
|
cert := findCert(ocspReq.SerialNumber)
|
|
data, _ := ocsp.CreateResponse(ca.cert, cert, ocspResp, ca.key)
|
|
|
|
w.WriteHeader(http.StatusOK)
|
|
_, _ = w.Write(data)
|
|
})
|
|
r.Post("/acme/finalize", func(w http.ResponseWriter, r *http.Request) {
|
|
var payload struct {
|
|
CSR string `json:"csr"`
|
|
}
|
|
readJWSPayload(r.Body, &payload)
|
|
bs, _ := base64.RawURLEncoding.DecodeString(payload.CSR)
|
|
csr, _ := x509.ParseCertificateRequest(bs)
|
|
tpl := &x509.Certificate{
|
|
SerialNumber: big.NewInt(time.Now().Unix()),
|
|
DNSNames: csr.DNSNames,
|
|
IPAddresses: csr.IPAddresses,
|
|
Subject: pkix.Name{
|
|
CommonName: csr.DNSNames[0],
|
|
},
|
|
NotBefore: time.Now(),
|
|
NotAfter: time.Now().Add(time.Second * 2),
|
|
|
|
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
|
|
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth},
|
|
BasicConstraintsValid: true,
|
|
IsCA: false,
|
|
|
|
IssuingCertificateURL: []string{srv.URL + "/certs/ca"},
|
|
OCSPServer: []string{srv.URL + "/ocsp/request"},
|
|
}
|
|
der, _ := x509.CreateCertificate(rand.Reader, tpl, ca.cert, csr.PublicKey, ca.key)
|
|
certBuffer.Reset()
|
|
_ = pem.Encode(&certBuffer, &pem.Block{Type: "CERTIFICATE", Bytes: der})
|
|
cert, _ := x509.ParseCertificate(der)
|
|
certs = append(certs, cert)
|
|
|
|
w.Header().Set("Replay-Nonce", "NONCE")
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.WriteHeader(http.StatusCreated)
|
|
_ = json.NewEncoder(w).Encode(M{
|
|
"status": "valid",
|
|
"finalize": srv.URL + "/acme/finalize",
|
|
"certificate": srv.URL + "/acme/certificate",
|
|
})
|
|
})
|
|
r.Post("/acme/certificate", func(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Set("Replay-Nonce", "NONCE")
|
|
w.Header().Set("Content-Type", "application/pem-certificate-chain")
|
|
w.WriteHeader(http.StatusOK)
|
|
_, _ = w.Write(certBuffer.Bytes())
|
|
})
|
|
r.Get("/certs/ca", func(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Set("Content-Type", "application/pkix-cert")
|
|
w.WriteHeader(http.StatusOK)
|
|
_, _ = w.Write(ca.cert.Raw)
|
|
})
|
|
return r
|
|
}
|
|
|
|
func TestConfig(t *testing.T) {
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
defer cancel()
|
|
|
|
var mockACME http.Handler
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
mockACME.ServeHTTP(w, r)
|
|
}))
|
|
defer srv.Close()
|
|
|
|
ca, err := newTestCA()
|
|
require.NoError(t, err)
|
|
|
|
mockACME = newMockACME(ca, srv)
|
|
|
|
tmpdir := filepath.Join(os.TempDir(), uuid.New().String())
|
|
_ = os.MkdirAll(tmpdir, 0o755)
|
|
defer os.RemoveAll(tmpdir)
|
|
|
|
li, err := net.Listen("tcp", "127.0.0.1:0")
|
|
if !assert.NoError(t, err) {
|
|
return
|
|
}
|
|
addr := li.Addr().String()
|
|
_ = li.Close()
|
|
|
|
to, err := config.ParseWeightedUrls("http://to.example.com")
|
|
require.NoError(t, err)
|
|
|
|
p1 := config.Policy{
|
|
From: "http://from.example.com", To: to,
|
|
}
|
|
_ = p1.Validate()
|
|
|
|
mgr, err := newManager(ctx, config.NewStaticSource(&config.Config{
|
|
Options: &config.Options{
|
|
AutocertOptions: config.AutocertOptions{
|
|
Enable: true,
|
|
UseStaging: true,
|
|
Email: "pomerium-test@example.com",
|
|
MustStaple: true,
|
|
Folder: tmpdir,
|
|
},
|
|
HTTPRedirectAddr: addr,
|
|
Policies: []config.Policy{p1},
|
|
},
|
|
}), certmagic.ACMEManager{
|
|
CA: srv.URL + "/acme/directory",
|
|
TestCA: srv.URL + "/acme/directory",
|
|
}, time.Millisecond*100)
|
|
if !assert.NoError(t, err) {
|
|
return
|
|
}
|
|
|
|
domainRenewed := make(chan bool)
|
|
ocspUpdated := make(chan bool)
|
|
|
|
var initialOCSPStaple []byte
|
|
var certValidTime *time.Time
|
|
mgr.OnConfigChange(ctx, func(ctx context.Context, cfg *config.Config) {
|
|
log.Info(ctx).Msg("OnConfigChange")
|
|
cert := cfg.AutoCertificates[0]
|
|
if initialOCSPStaple == nil {
|
|
initialOCSPStaple = cert.OCSPStaple
|
|
} else {
|
|
if bytes.Compare(initialOCSPStaple, cert.OCSPStaple) != 0 {
|
|
log.Info(ctx).Msg("OCSP updated")
|
|
ocspUpdated <- true
|
|
}
|
|
}
|
|
if certValidTime == nil {
|
|
certValidTime = &cert.Leaf.NotAfter
|
|
} else {
|
|
if !certValidTime.Equal(cert.Leaf.NotAfter) {
|
|
log.Info(ctx).Msg("domain renewed")
|
|
domainRenewed <- true
|
|
}
|
|
}
|
|
})
|
|
|
|
domainRenewedOK := false
|
|
ocspUpdatedOK := false
|
|
|
|
for !domainRenewedOK || !ocspUpdatedOK {
|
|
select {
|
|
case <-time.After(time.Second * 10):
|
|
t.Error("timeout waiting for certs renewal")
|
|
return
|
|
case domainRenewedOK = <-domainRenewed:
|
|
case ocspUpdatedOK = <-ocspUpdated:
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestRedirect(t *testing.T) {
|
|
li, err := net.Listen("tcp", "127.0.0.1:0")
|
|
if !assert.NoError(t, err) {
|
|
return
|
|
}
|
|
addr := li.Addr().String()
|
|
_ = li.Close()
|
|
|
|
src := config.NewStaticSource(&config.Config{
|
|
Options: &config.Options{
|
|
HTTPRedirectAddr: addr,
|
|
SetResponseHeaders: map[string]string{
|
|
"X-Frame-Options": "SAMEORIGIN",
|
|
"X-XSS-Protection": "1; mode=block",
|
|
"Strict-Transport-Security": "max-age=31536000; includeSubDomains; preload",
|
|
},
|
|
},
|
|
})
|
|
_, err = New(src)
|
|
if !assert.NoError(t, err) {
|
|
return
|
|
}
|
|
err = waitFor(addr)
|
|
if !assert.NoError(t, err) {
|
|
return
|
|
}
|
|
|
|
client := &http.Client{
|
|
CheckRedirect: func(req *http.Request, via []*http.Request) error {
|
|
return http.ErrUseLastResponse
|
|
},
|
|
}
|
|
|
|
res, err := client.Get(fmt.Sprintf("http://%s", addr))
|
|
if !assert.NoError(t, err) {
|
|
return
|
|
}
|
|
defer res.Body.Close()
|
|
|
|
assert.Equal(t, http.StatusMovedPermanently, res.StatusCode, "should redirect to https")
|
|
for k, v := range src.GetConfig().Options.SetResponseHeaders {
|
|
assert.NotEqual(t, v, res.Header.Get(k), "should ignore options header")
|
|
}
|
|
}
|
|
|
|
func waitFor(addr string) error {
|
|
var err error
|
|
deadline := time.Now().Add(time.Second * 30)
|
|
for time.Now().Before(deadline) {
|
|
var conn net.Conn
|
|
conn, err = net.Dial("tcp", addr)
|
|
if err == nil {
|
|
conn.Close()
|
|
return nil
|
|
}
|
|
time.Sleep(time.Second)
|
|
}
|
|
return err
|
|
}
|
|
|
|
func readJWSPayload(r io.Reader, dst interface{}) {
|
|
var req struct {
|
|
Protected string `json:"protected"`
|
|
Payload string `json:"payload"`
|
|
Signature string `json:"signature"`
|
|
}
|
|
_ = json.NewDecoder(r).Decode(&req)
|
|
|
|
bs, _ := base64.RawURLEncoding.DecodeString(req.Payload)
|
|
_ = json.Unmarshal(bs, dst)
|
|
}
|
|
|
|
func newACMEManager() *certmagic.ACMEManager {
|
|
return &certmagic.ACMEManager{
|
|
CA: certmagic.DefaultACME.CA,
|
|
TestCA: certmagic.DefaultACME.TestCA,
|
|
}
|
|
}
|
|
|
|
func Test_configureCertificateAuthority(t *testing.T) {
|
|
type args struct {
|
|
acmeMgr *certmagic.ACMEManager
|
|
opts config.AutocertOptions
|
|
}
|
|
type test struct {
|
|
args args
|
|
expected *certmagic.ACMEManager
|
|
wantErr bool
|
|
}
|
|
var tests = map[string]func(t *testing.T) test{
|
|
"ok/default": func(t *testing.T) test {
|
|
return test{
|
|
args: args{
|
|
acmeMgr: newACMEManager(),
|
|
opts: config.AutocertOptions{},
|
|
},
|
|
expected: &certmagic.ACMEManager{
|
|
Agreed: true,
|
|
CA: certmagic.DefaultACME.CA,
|
|
TestCA: certmagic.DefaultACME.TestCA,
|
|
},
|
|
wantErr: false,
|
|
}
|
|
},
|
|
"ok/staging": func(t *testing.T) test {
|
|
return test{
|
|
args: args{
|
|
acmeMgr: newACMEManager(),
|
|
opts: config.AutocertOptions{
|
|
UseStaging: true,
|
|
},
|
|
},
|
|
expected: &certmagic.ACMEManager{
|
|
Agreed: true,
|
|
CA: certmagic.DefaultACME.TestCA,
|
|
TestCA: certmagic.DefaultACME.TestCA,
|
|
},
|
|
wantErr: false,
|
|
}
|
|
},
|
|
"ok/custom-ca-staging": func(t *testing.T) test {
|
|
return test{
|
|
args: args{
|
|
acmeMgr: newACMEManager(),
|
|
opts: config.AutocertOptions{
|
|
CA: "test-ca.example.com/directory",
|
|
Email: "test@example.com",
|
|
UseStaging: true,
|
|
},
|
|
},
|
|
expected: &certmagic.ACMEManager{
|
|
Agreed: true,
|
|
CA: "test-ca.example.com/directory",
|
|
Email: "test@example.com",
|
|
TestCA: certmagic.DefaultACME.TestCA,
|
|
},
|
|
wantErr: false,
|
|
}
|
|
},
|
|
}
|
|
for name, run := range tests {
|
|
tc := run(t)
|
|
t.Run(name, func(t *testing.T) {
|
|
if err := configureCertificateAuthority(tc.args.acmeMgr, tc.args.opts); (err != nil) != tc.wantErr {
|
|
t.Errorf("configureCertificateAuthority() error = %v, wantErr %v", err, tc.wantErr)
|
|
}
|
|
if !cmp.Equal(tc.expected, tc.args.acmeMgr, cmpopts.IgnoreUnexported(certmagic.ACMEManager{})) {
|
|
t.Errorf("configureCertificateAuthority() diff = %s", cmp.Diff(tc.expected, tc.args.acmeMgr, cmpopts.IgnoreUnexported(certmagic.ACMEManager{})))
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_configureExternalAccountBinding(t *testing.T) {
|
|
type args struct {
|
|
acmeMgr *certmagic.ACMEManager
|
|
opts config.AutocertOptions
|
|
}
|
|
type test struct {
|
|
args args
|
|
expected *certmagic.ACMEManager
|
|
wantErr bool
|
|
}
|
|
var tests = map[string]func(t *testing.T) test{
|
|
"ok": func(t *testing.T) test {
|
|
return test{
|
|
args: args{
|
|
acmeMgr: newACMEManager(),
|
|
opts: config.AutocertOptions{
|
|
EABKeyID: "keyID",
|
|
EABMACKey: "29D7t6-mOuEV5vvBRX0UYF5T7x6fomidhM1kMJco-yw",
|
|
},
|
|
},
|
|
expected: &certmagic.ACMEManager{
|
|
CA: certmagic.DefaultACME.CA,
|
|
TestCA: certmagic.DefaultACME.TestCA,
|
|
ExternalAccount: &acme.EAB{
|
|
KeyID: "keyID",
|
|
MACKey: "29D7t6-mOuEV5vvBRX0UYF5T7x6fomidhM1kMJco-yw",
|
|
},
|
|
},
|
|
wantErr: false,
|
|
}
|
|
},
|
|
"fail/error-decoding-mac-key": func(t *testing.T) test {
|
|
return test{
|
|
args: args{
|
|
acmeMgr: newACMEManager(),
|
|
opts: config.AutocertOptions{
|
|
EABKeyID: "keyID",
|
|
EABMACKey: ">invalid-base-64-data<",
|
|
},
|
|
},
|
|
wantErr: true,
|
|
}
|
|
},
|
|
}
|
|
|
|
for name, run := range tests {
|
|
tc := run(t)
|
|
t.Run(name, func(t *testing.T) {
|
|
err := configureExternalAccountBinding(tc.args.acmeMgr, tc.args.opts)
|
|
if (err != nil) != tc.wantErr {
|
|
t.Errorf("configureExternalAccountBinding() error = %v, wantErr %v", err, tc.wantErr)
|
|
}
|
|
if err == nil && !cmp.Equal(tc.expected, tc.args.acmeMgr, cmpopts.IgnoreUnexported(certmagic.ACMEManager{})) {
|
|
t.Errorf("configureCertificateAuthority() diff = %s", cmp.Diff(tc.expected, tc.args.acmeMgr, cmpopts.IgnoreUnexported(certmagic.ACMEManager{})))
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_configureTrustedRoots(t *testing.T) {
|
|
ca, err := newTestCA()
|
|
require.NoError(t, err)
|
|
type args struct {
|
|
acmeMgr *certmagic.ACMEManager
|
|
opts config.AutocertOptions
|
|
}
|
|
type test struct {
|
|
args args
|
|
expected *certmagic.ACMEManager
|
|
wantErr bool
|
|
cleanup func()
|
|
}
|
|
var tests = map[string]func(t *testing.T) test{
|
|
"ok/pem": func(t *testing.T) test {
|
|
copy, err := x509.SystemCertPool()
|
|
require.NoError(t, err)
|
|
ok := copy.AppendCertsFromPEM(ca.certPEM)
|
|
require.Equal(t, true, ok)
|
|
return test{
|
|
args: args{
|
|
acmeMgr: newACMEManager(),
|
|
opts: config.AutocertOptions{
|
|
TrustedCA: base64.StdEncoding.EncodeToString(ca.certPEM),
|
|
},
|
|
},
|
|
expected: &certmagic.ACMEManager{
|
|
CA: certmagic.DefaultACME.CA,
|
|
TestCA: certmagic.DefaultACME.TestCA,
|
|
TrustedRoots: copy,
|
|
},
|
|
wantErr: false,
|
|
}
|
|
},
|
|
"ok/file": func(t *testing.T) test {
|
|
copy, err := x509.SystemCertPool()
|
|
require.NoError(t, err)
|
|
ok := copy.AppendCertsFromPEM(ca.certPEM)
|
|
require.Equal(t, true, ok)
|
|
f, err := ioutil.TempFile("", "pomerium-test-ca")
|
|
require.NoError(t, err)
|
|
n, err := f.Write(ca.certPEM)
|
|
require.NoError(t, err)
|
|
require.Equal(t, len(ca.certPEM), n)
|
|
return test{
|
|
args: args{
|
|
acmeMgr: newACMEManager(),
|
|
opts: config.AutocertOptions{
|
|
TrustedCAFile: f.Name(),
|
|
},
|
|
},
|
|
expected: &certmagic.ACMEManager{
|
|
CA: certmagic.DefaultACME.CA,
|
|
TestCA: certmagic.DefaultACME.TestCA,
|
|
TrustedRoots: copy,
|
|
},
|
|
wantErr: false,
|
|
cleanup: func() {
|
|
os.Remove(f.Name())
|
|
},
|
|
}
|
|
},
|
|
"fail/pem": func(t *testing.T) test {
|
|
copy, err := x509.SystemCertPool()
|
|
require.NoError(t, err)
|
|
return test{
|
|
args: args{
|
|
acmeMgr: newACMEManager(),
|
|
opts: config.AutocertOptions{
|
|
TrustedCA: ">invalid-base-64-ca-pem<",
|
|
},
|
|
},
|
|
expected: &certmagic.ACMEManager{
|
|
CA: certmagic.DefaultACME.CA,
|
|
TestCA: certmagic.DefaultACME.TestCA,
|
|
TrustedRoots: copy,
|
|
},
|
|
wantErr: true,
|
|
}
|
|
},
|
|
"fail/file": func(t *testing.T) test {
|
|
copy, err := x509.SystemCertPool()
|
|
require.NoError(t, err)
|
|
return test{
|
|
args: args{
|
|
acmeMgr: newACMEManager(),
|
|
opts: config.AutocertOptions{
|
|
TrustedCAFile: "some-non-existing-file",
|
|
},
|
|
},
|
|
expected: &certmagic.ACMEManager{
|
|
CA: certmagic.DefaultACME.CA,
|
|
TestCA: certmagic.DefaultACME.TestCA,
|
|
TrustedRoots: copy,
|
|
},
|
|
wantErr: true,
|
|
}
|
|
},
|
|
}
|
|
for name, run := range tests {
|
|
tc := run(t)
|
|
t.Run(name, func(t *testing.T) {
|
|
err := configureTrustedRoots(tc.args.acmeMgr, tc.args.opts)
|
|
if (err != nil) != tc.wantErr {
|
|
t.Errorf("configureTrustedRoots() error = %v, wantErr %v", err, tc.wantErr)
|
|
}
|
|
if err == nil && !cmp.Equal(tc.expected, tc.args.acmeMgr, cmpopts.IgnoreUnexported(certmagic.ACMEManager{}, x509.CertPool{})) {
|
|
t.Errorf("configureCertificateAuthority() diff = %s", cmp.Diff(tc.expected, tc.args.acmeMgr, cmpopts.IgnoreUnexported(certmagic.ACMEManager{}, x509.CertPool{})))
|
|
}
|
|
if err == nil && !cmp.Equal(tc.expected.TrustedRoots.Subjects(), tc.args.acmeMgr.TrustedRoots.Subjects()) {
|
|
t.Errorf("configureCertificateAuthority() subjects diff = %s", cmp.Diff(tc.expected.TrustedRoots.Subjects(), tc.args.acmeMgr.TrustedRoots.Subjects()))
|
|
}
|
|
if tc.cleanup != nil {
|
|
tc.cleanup()
|
|
}
|
|
})
|
|
}
|
|
}
|