test: Add integration tests for MPIC validation (#8102)
- Update the chall-test-srv-client to make DNS events and DNS01 methods more convenient - Add an integration test that counts DCV and CAA checks for each validation method Part of #7963
This commit is contained in:
parent
5cc8a77ce3
commit
bc39780908
|
|
@ -2,6 +2,8 @@ package challtestsrvclient
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/base64"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
|
@ -354,13 +356,17 @@ func (c *Client) RemoveServfailResponse(host string) ([]byte, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddDNS01Response adds an ACME DNS-01 challenge response for the provided host
|
// AddDNS01Response adds an ACME DNS-01 challenge response for the provided host
|
||||||
// to the challenge test server's DNS interfaces. The provided value will be
|
// to the challenge test server's DNS interfaces. The value is hashed and
|
||||||
// served for TXT queries for _acme-challenge.<host>. Any failure returns an
|
// base64-encoded using RawURLEncoding, and served for TXT queries to
|
||||||
// error that includes both the relevant operation and the payload.
|
// _acme-challenge.<host>. Any failure returns an error that includes both the
|
||||||
|
// relevant operation and the payload.
|
||||||
func (c *Client) AddDNS01Response(host, value string) ([]byte, error) {
|
func (c *Client) AddDNS01Response(host, value string) ([]byte, error) {
|
||||||
|
host = "_acme-challenge." + host
|
||||||
if !strings.HasSuffix(host, ".") {
|
if !strings.HasSuffix(host, ".") {
|
||||||
host += "."
|
host += "."
|
||||||
}
|
}
|
||||||
|
h := sha256.Sum256([]byte(value))
|
||||||
|
value = base64.RawURLEncoding.EncodeToString(h[:])
|
||||||
payload := map[string]string{"host": host, "value": value}
|
payload := map[string]string{"host": host, "value": value}
|
||||||
resp, err := c.postURL(addTXT, payload)
|
resp, err := c.postURL(addTXT, payload)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -376,6 +382,12 @@ func (c *Client) AddDNS01Response(host, value string) ([]byte, error) {
|
||||||
// provided host from the challenge test server's DNS interfaces. Any failure
|
// provided host from the challenge test server's DNS interfaces. Any failure
|
||||||
// returns an error that includes both the relevant operation and the payload.
|
// returns an error that includes both the relevant operation and the payload.
|
||||||
func (c *Client) RemoveDNS01Response(host string) ([]byte, error) {
|
func (c *Client) RemoveDNS01Response(host string) ([]byte, error) {
|
||||||
|
if !strings.HasPrefix(host, "_acme-challenge.") {
|
||||||
|
host = "_acme-challenge." + host
|
||||||
|
}
|
||||||
|
if !strings.HasSuffix(host, ".") {
|
||||||
|
host += "."
|
||||||
|
}
|
||||||
payload := map[string]string{"host": host}
|
payload := map[string]string{"host": host}
|
||||||
resp, err := c.postURL(delTXT, payload)
|
resp, err := c.postURL(delTXT, payload)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -391,8 +403,8 @@ func (c *Client) RemoveDNS01Response(host string) ([]byte, error) {
|
||||||
type DNSRequest struct {
|
type DNSRequest struct {
|
||||||
Question struct {
|
Question struct {
|
||||||
Name string `json:"Name"`
|
Name string `json:"Name"`
|
||||||
Qtype int `json:"Qtype"`
|
Qtype uint16 `json:"Qtype"`
|
||||||
Qclass int `json:"Qclass"`
|
Qclass uint16 `json:"Qclass"`
|
||||||
} `json:"Question"`
|
} `json:"Question"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,8 +13,226 @@ import (
|
||||||
|
|
||||||
"github.com/eggsampler/acme/v3"
|
"github.com/eggsampler/acme/v3"
|
||||||
"github.com/letsencrypt/boulder/test/vars"
|
"github.com/letsencrypt/boulder/test/vars"
|
||||||
|
"github.com/miekg/dns"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func TestMPICTLSALPN01(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
client, err := makeClient()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("creating acme client: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
domain := randomDomain(t)
|
||||||
|
|
||||||
|
order, err := client.Client.NewOrder(client.Account, []acme.Identifier{{Type: "dns", Value: domain}})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("creating order: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
authz, err := client.Client.FetchAuthorization(client.Account, order.Authorizations[0])
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("fetching authorization: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
chal, ok := authz.ChallengeMap[acme.ChallengeTypeTLSALPN01]
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("no TLS-ALPN-01 challenge found in %#v", authz)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = testSrvClient.AddARecord(domain, []string{"10.88.88.88"})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("adding A record: %s", err)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
testSrvClient.RemoveARecord(domain)
|
||||||
|
}()
|
||||||
|
|
||||||
|
_, err = testSrvClient.AddTLSALPN01Response(domain, chal.KeyAuthorization)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
_, err = testSrvClient.RemoveTLSALPN01Response(domain)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
chal, err = client.Client.UpdateChallenge(client.Account, chal)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("completing TLS-ALPN-01 validation: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
validationEvents, err := testSrvClient.TLSALPN01RequestHistory(domain)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if len(validationEvents) != 4 {
|
||||||
|
t.Errorf("expected 4 validation events (VA=1 RVAs=3), got %d", len(validationEvents))
|
||||||
|
}
|
||||||
|
|
||||||
|
dnsEvents, err := testSrvClient.DNSRequestHistory(domain)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
caaCount := 0
|
||||||
|
for _, event := range dnsEvents {
|
||||||
|
if event.Question.Qtype == dns.TypeCAA {
|
||||||
|
caaCount++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if caaCount != 4 {
|
||||||
|
t.Fatalf("expected 4 CAA validation events (VA=1 RVAs=3), got %d", caaCount)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMPICDNS01(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
client, err := makeClient()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("creating acme client: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
domain := randomDomain(t)
|
||||||
|
|
||||||
|
order, err := client.Client.NewOrder(client.Account, []acme.Identifier{{Type: "dns", Value: domain}})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("creating order: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
authz, err := client.Client.FetchAuthorization(client.Account, order.Authorizations[0])
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("fetching authorization: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
chal, ok := authz.ChallengeMap[acme.ChallengeTypeDNS01]
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("no DNS challenge found in %#v", authz)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = testSrvClient.AddDNS01Response(domain, chal.KeyAuthorization)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
_, err = testSrvClient.RemoveDNS01Response(domain)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
chal, err = client.Client.UpdateChallenge(client.Account, chal)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("completing DNS-01 validation: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
challDomainDNSEvents, err := testSrvClient.DNSRequestHistory("_acme-challenge." + domain)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
validationCount := 0
|
||||||
|
for _, event := range challDomainDNSEvents {
|
||||||
|
if event.Question.Qtype == dns.TypeTXT && event.Question.Name == "_acme-challenge."+domain+"." {
|
||||||
|
validationCount++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if validationCount != 4 {
|
||||||
|
t.Errorf("expected 4 validation events (VA=1 RVAs=3), got %d", validationCount)
|
||||||
|
}
|
||||||
|
|
||||||
|
domainDNSEvents, err := testSrvClient.DNSRequestHistory(domain)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
caaCount := 0
|
||||||
|
for _, event := range domainDNSEvents {
|
||||||
|
if event.Question.Qtype == dns.TypeCAA {
|
||||||
|
caaCount++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if caaCount != 4 {
|
||||||
|
t.Fatalf("expected 4 CAA validation events (VA=1 RVAs=3), got %d", caaCount)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMPICHTTP01(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
client, err := makeClient()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("creating acme client: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
domain := randomDomain(t)
|
||||||
|
|
||||||
|
order, err := client.Client.NewOrder(client.Account, []acme.Identifier{{Type: "dns", Value: domain}})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("creating order: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
authz, err := client.Client.FetchAuthorization(client.Account, order.Authorizations[0])
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("fetching authorization: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
chal, ok := authz.ChallengeMap[acme.ChallengeTypeHTTP01]
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("no HTTP challenge found in %#v", authz)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = testSrvClient.AddHTTP01Response(chal.Token, chal.KeyAuthorization)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
_, err = testSrvClient.RemoveHTTP01Response(chal.Token)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
chal, err = client.Client.UpdateChallenge(client.Account, chal)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("completing HTTP-01 validation: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
validationEvents, err := testSrvClient.HTTPRequestHistory(domain)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
validationCount := 0
|
||||||
|
for _, event := range validationEvents {
|
||||||
|
if event.URL == "/.well-known/acme-challenge/"+chal.Token {
|
||||||
|
validationCount++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if validationCount != 4 {
|
||||||
|
t.Errorf("expected 4 validation events (VA=1 RVAs=3), got %d", validationCount)
|
||||||
|
}
|
||||||
|
|
||||||
|
dnsEvents, err := testSrvClient.DNSRequestHistory(domain)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
caaCount := 0
|
||||||
|
for _, event := range dnsEvents {
|
||||||
|
if event.Question.Qtype == dns.TypeCAA {
|
||||||
|
caaCount++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if caaCount != 4 {
|
||||||
|
t.Fatalf("expected 4 CAA validation events (VA=1 RVAs=3), got %d", caaCount)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestCAARechecking(t *testing.T) {
|
func TestCAARechecking(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue