policy: Support IP address identifiers (#8173)

Add `pa.validIP` to test IP address validity & absence from IANA
reservations.

Modify `pa.WillingToIssue` and `pa.WellFormedIdentifiers` to support IP
address identifiers.

Add a map of allowed identifier types to the `pa` config.

Part of #8137
This commit is contained in:
James Renken 2025-05-14 13:49:51 -07:00 committed by GitHub
parent 4d28e010f6
commit 648ab05b37
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 479 additions and 178 deletions

View File

@ -147,7 +147,7 @@ func setup(t *testing.T) *testCtx {
fc := clock.NewFake()
fc.Add(1 * time.Hour)
pa, err := policy.New(nil, blog.NewMock())
pa, err := policy.New(nil, nil, blog.NewMock())
test.AssertNotError(t, err, "Couldn't create PA")
err = pa.LoadHostnamePolicyFile("../test/hostname-policy.yaml")
test.AssertNotError(t, err, "Couldn't set hostname policy")

View File

@ -164,8 +164,9 @@ func main() {
metrics := ca.NewCAMetrics(scope)
cmd.FailOnError(c.PA.CheckChallenges(), "Invalid PA configuration")
cmd.FailOnError(c.PA.CheckIdentifiers(), "Invalid PA configuration")
pa, err := policy.New(c.PA.Challenges, logger)
pa, err := policy.New(c.PA.Identifiers, c.PA.Challenges, logger)
cmd.FailOnError(err, "Couldn't create PA")
if c.CA.HostnamePolicyFile == "" {

View File

@ -166,8 +166,9 @@ func main() {
// Validate PA config and set defaults if needed
cmd.FailOnError(c.PA.CheckChallenges(), "Invalid PA configuration")
cmd.FailOnError(c.PA.CheckIdentifiers(), "Invalid PA configuration")
pa, err := policy.New(c.PA.Challenges, logger)
pa, err := policy.New(c.PA.Identifiers, c.PA.Challenges, logger)
cmd.FailOnError(err, "Couldn't create PA")
if c.RA.HostnamePolicyFile == "" {

View File

@ -562,6 +562,7 @@ func main() {
// Validate PA config and set defaults if needed.
cmd.FailOnError(config.PA.CheckChallenges(), "Invalid PA configuration")
cmd.FailOnError(config.PA.CheckIdentifiers(), "Invalid PA configuration")
kp, err := sagoodkey.NewPolicy(&config.CertChecker.GoodKey, nil)
cmd.FailOnError(err, "Unable to create key policy")
@ -575,7 +576,7 @@ func main() {
})
prometheus.DefaultRegisterer.MustRegister(checkerLatency)
pa, err := policy.New(config.PA.Challenges, logger)
pa, err := policy.New(config.PA.Identifiers, config.PA.Challenges, logger)
cmd.FailOnError(err, "Failed to create PA")
err = pa.LoadHostnamePolicyFile(config.CertChecker.HostnamePolicyFile)

View File

@ -31,6 +31,7 @@ import (
"github.com/letsencrypt/boulder/ctpolicy/loglist"
"github.com/letsencrypt/boulder/goodkey"
"github.com/letsencrypt/boulder/goodkey/sagoodkey"
"github.com/letsencrypt/boulder/identifier"
"github.com/letsencrypt/boulder/linter"
blog "github.com/letsencrypt/boulder/log"
"github.com/letsencrypt/boulder/metrics"
@ -52,7 +53,10 @@ var (
func init() {
var err error
pa, err = policy.New(map[core.AcmeChallenge]bool{}, blog.NewMock())
pa, err = policy.New(
map[identifier.IdentifierType]bool{identifier.TypeDNS: true, identifier.TypeIP: true},
map[core.AcmeChallenge]bool{},
blog.NewMock())
if err != nil {
log.Fatal(err)
}

View File

@ -16,6 +16,7 @@ import (
"github.com/letsencrypt/boulder/config"
"github.com/letsencrypt/boulder/core"
"github.com/letsencrypt/boulder/identifier"
)
// PasswordConfig contains a path to a file containing a password.
@ -99,8 +100,9 @@ type SMTPConfig struct {
// database, what policies it should enforce, and what challenges
// it should offer.
type PAConfig struct {
DBConfig `validate:"-"`
Challenges map[core.AcmeChallenge]bool `validate:"omitempty,dive,keys,oneof=http-01 dns-01 tls-alpn-01,endkeys"`
DBConfig `validate:"-"`
Challenges map[core.AcmeChallenge]bool `validate:"omitempty,dive,keys,oneof=http-01 dns-01 tls-alpn-01,endkeys"`
Identifiers map[identifier.IdentifierType]bool `validate:"omitempty,dive,keys,oneof=dns ip,endkeys"`
}
// CheckChallenges checks whether the list of challenges in the PA config
@ -117,6 +119,17 @@ func (pc PAConfig) CheckChallenges() error {
return nil
}
// CheckIdentifiers checks whether the list of identifiers in the PA config
// actually contains valid identifier type names
func (pc PAConfig) CheckIdentifiers() error {
for i := range pc.Identifiers {
if !i.IsValid() {
return fmt.Errorf("invalid identifier type in PA config: %s", i)
}
}
return nil
}
// HostnamePolicyConfig specifies a file from which to load a policy regarding
// what hostnames to issue for.
type HostnamePolicyConfig struct {

View File

@ -40,7 +40,7 @@ func main() {
scanner := bufio.NewScanner(input)
logger := cmd.NewLogger(cmd.SyslogConfig{StdoutLevel: 7})
logger.Info(cmd.VersionString())
pa, err := policy.New(nil, logger)
pa, err := policy.New(nil, nil, logger)
if err != nil {
log.Fatal(err)
}

View File

@ -23,22 +23,24 @@ var (
validPAConfig = []byte(`{
"dbConnect": "dummyDBConnect",
"enforcePolicyWhitelist": false,
"challenges": { "http-01": true }
"challenges": { "http-01": true },
"identifiers": { "dns": true, "ip": true }
}`)
invalidPAConfig = []byte(`{
"dbConnect": "dummyDBConnect",
"enforcePolicyWhitelist": false,
"challenges": { "nonsense": true }
"challenges": { "nonsense": true },
"identifiers": { "openpgp": true }
}`)
noChallengesPAConfig = []byte(`{
noChallengesIdentsPAConfig = []byte(`{
"dbConnect": "dummyDBConnect",
"enforcePolicyWhitelist": false
}`)
emptyChallengesPAConfig = []byte(`{
emptyChallengesIdentsPAConfig = []byte(`{
"dbConnect": "dummyDBConnect",
"enforcePolicyWhitelist": false,
"challenges": {}
"challenges": {},
"identifiers": {}
}`)
)
@ -47,21 +49,25 @@ func TestPAConfigUnmarshal(t *testing.T) {
err := json.Unmarshal(validPAConfig, &pc1)
test.AssertNotError(t, err, "Failed to unmarshal PAConfig")
test.AssertNotError(t, pc1.CheckChallenges(), "Flagged valid challenges as bad")
test.AssertNotError(t, pc1.CheckIdentifiers(), "Flagged valid identifiers as bad")
var pc2 PAConfig
err = json.Unmarshal(invalidPAConfig, &pc2)
test.AssertNotError(t, err, "Failed to unmarshal PAConfig")
test.AssertError(t, pc2.CheckChallenges(), "Considered invalid challenges as good")
test.AssertError(t, pc2.CheckIdentifiers(), "Considered invalid identifiers as good")
var pc3 PAConfig
err = json.Unmarshal(noChallengesPAConfig, &pc3)
err = json.Unmarshal(noChallengesIdentsPAConfig, &pc3)
test.AssertNotError(t, err, "Failed to unmarshal PAConfig")
test.AssertError(t, pc3.CheckChallenges(), "Disallow empty challenges map")
test.AssertNotError(t, pc3.CheckIdentifiers(), "Disallowed empty identifiers map")
var pc4 PAConfig
err = json.Unmarshal(emptyChallengesPAConfig, &pc4)
err = json.Unmarshal(emptyChallengesIdentsPAConfig, &pc4)
test.AssertNotError(t, err, "Failed to unmarshal PAConfig")
test.AssertError(t, pc4.CheckChallenges(), "Disallow empty challenges map")
test.AssertNotError(t, pc4.CheckIdentifiers(), "Disallowed empty identifiers map")
}
func TestMysqlLogger(t *testing.T) {

View File

@ -30,6 +30,16 @@ const (
TypeIP = IdentifierType("ip")
)
// IsValid tests whether the identifier type is known
func (i IdentifierType) IsValid() bool {
switch i {
case TypeDNS, TypeIP:
return true
default:
return false
}
}
// ACMEIdentifier is a struct encoding an identifier that can be validated. The
// protocol allows for different types of identifier to be supported (DNS
// names, IP addresses, etc.), but currently we only support RFC 8555 DNS type

View File

@ -16,6 +16,7 @@ import (
"golang.org/x/net/idna"
"golang.org/x/text/unicode/norm"
"github.com/letsencrypt/boulder/bdns"
"github.com/letsencrypt/boulder/core"
berrors "github.com/letsencrypt/boulder/errors"
"github.com/letsencrypt/boulder/iana"
@ -33,14 +34,24 @@ type AuthorityImpl struct {
wildcardExactBlocklist map[string]bool
blocklistMu sync.RWMutex
enabledChallenges map[core.AcmeChallenge]bool
enabledChallenges map[core.AcmeChallenge]bool
enabledIdentifiers map[identifier.IdentifierType]bool
}
// New constructs a Policy Authority.
func New(challengeTypes map[core.AcmeChallenge]bool, log blog.Logger) (*AuthorityImpl, error) {
func New(identifierTypes map[identifier.IdentifierType]bool, challengeTypes map[core.AcmeChallenge]bool, log blog.Logger) (*AuthorityImpl, error) {
// If identifierTypes are not configured (i.e. nil), default to allowing DNS
// identifiers. This default is temporary, to improve deployability.
//
// TODO(#8184): Remove this default.
if identifierTypes == nil {
identifierTypes = map[identifier.IdentifierType]bool{identifier.TypeDNS: true}
}
return &AuthorityImpl{
log: log,
enabledChallenges: challengeTypes,
log: log,
enabledChallenges: challengeTypes,
enabledIdentifiers: identifierTypes,
}, nil
}
@ -167,9 +178,11 @@ var (
errPolicyForbidden = berrors.RejectedIdentifierError("The ACME server refuses to issue a certificate for this domain name, because it is forbidden by policy")
errInvalidDNSCharacter = berrors.MalformedError("Domain name contains an invalid character")
errNameTooLong = berrors.MalformedError("Domain name is longer than 253 bytes")
errIPAddress = berrors.MalformedError("The ACME server can not issue a certificate for an IP address")
errIPAddressInDNS = berrors.MalformedError("Identifier type is DNS but value is an IP address")
errIPInvalid = berrors.MalformedError("IP address is invalid")
errIPSpecialPurpose = berrors.MalformedError("IP address is in a special-purpose address block")
errTooManyLabels = berrors.MalformedError("Domain name has more than 10 labels (parts)")
errEmptyName = berrors.MalformedError("Domain name is empty")
errEmptyIdentifier = berrors.MalformedError("Identifier value (name) is empty")
errNameEndsInDot = berrors.MalformedError("Domain name ends in a dot")
errTooFewLabels = berrors.MalformedError("Domain name needs at least one dot")
errLabelTooShort = berrors.MalformedError("Domain name can not have two dots in a row")
@ -180,7 +193,7 @@ var (
errMalformedWildcard = berrors.MalformedError("Domain name contains an invalid wildcard. A wildcard is only permitted before the first dot in a domain name")
errICANNTLDWildcard = berrors.MalformedError("Domain name is a wildcard for an ICANN TLD")
errWildcardNotSupported = berrors.MalformedError("Wildcard domain names are not supported")
errUnsupportedIdent = berrors.MalformedError("invalid non-DNS type identifier")
errUnsupportedIdent = berrors.MalformedError("Invalid identifier type")
)
// validNonWildcardDomain checks that a domain isn't:
@ -198,7 +211,7 @@ var (
// It does NOT ensure that the domain is absent from any PA blocked lists.
func validNonWildcardDomain(domain string) error {
if domain == "" {
return errEmptyName
return errEmptyIdentifier
}
if strings.HasPrefix(domain, "*.") {
@ -216,7 +229,7 @@ func validNonWildcardDomain(domain string) error {
}
if ip := net.ParseIP(domain); ip != nil {
return errIPAddress
return errIPAddressInDNS
}
if strings.HasSuffix(domain, ".") {
@ -319,6 +332,34 @@ func ValidDomain(domain string) error {
return validNonWildcardDomain(baseDomain)
}
// validIP checks that an IP address:
// - isn't empty
// - is an IPv4 or IPv6 address
// - isn't in an IANA special-purpose address registry
//
// It does NOT ensure that the IP address is absent from any PA blocked lists.
func validIP(ip string) error {
if ip == "" {
return errEmptyIdentifier
}
// Check the output of net.IP.String(), to ensure the input complied with
// RFC 8738, Sec. 3. ("The identifier value MUST contain the textual form of
// the address as defined in RFC 1123, Sec. 2.1 for IPv4 and in RFC 5952,
// Sec. 4 for IPv6.") ParseIP() will accept a non-compliant but otherwise
// valid string; String() will output a compliant string.
parsedIP := net.ParseIP(ip)
if parsedIP == nil || parsedIP.String() != ip {
return errIPInvalid
}
if bdns.IsReservedIP(parsedIP) {
return errIPSpecialPurpose
}
return nil
}
// forbiddenMailDomains is a map of domain names we do not allow after the
// @ symbol in contact mailto addresses. These are frequently used when
// copy-pasting example configurations and would not result in expiration
@ -390,42 +431,50 @@ func (pa *AuthorityImpl) WillingToIssue(idents identifier.ACMEIdentifiers) error
var subErrors []berrors.SubBoulderError
for _, ident := range idents {
if ident.Type != identifier.TypeDNS {
subErrors = append(subErrors, subError(ident, errUnsupportedIdent))
if !pa.IdentifierTypeEnabled(ident.Type) {
subErrors = append(subErrors, subError(ident, berrors.RejectedIdentifierError("The ACME server has disabled this identifier type")))
continue
}
if strings.Count(ident.Value, "*") > 0 {
// The base domain is the wildcard request with the `*.` prefix removed
baseDomain := strings.TrimPrefix(ident.Value, "*.")
// Only DNS identifiers are subject to wildcard and blocklist checks.
// Unsupported identifier types will have been caught by
// WellFormedIdentifiers().
//
// TODO(#7311): We may want to implement IP address blocklists too.
if ident.Type == identifier.TypeDNS {
if strings.Count(ident.Value, "*") > 0 {
// The base domain is the wildcard request with the `*.` prefix removed
baseDomain := strings.TrimPrefix(ident.Value, "*.")
// The base domain can't be in the wildcard exact blocklist
err = pa.checkWildcardHostList(baseDomain)
// The base domain can't be in the wildcard exact blocklist
err = pa.checkWildcardHostList(baseDomain)
if err != nil {
subErrors = append(subErrors, subError(ident, err))
continue
}
}
// For both wildcard and non-wildcard domains, check whether any parent domain
// name is on the regular blocklist.
err := pa.checkHostLists(ident.Value)
if err != nil {
subErrors = append(subErrors, subError(ident, err))
continue
}
}
// For both wildcard and non-wildcard domains, check whether any parent domain
// name is on the regular blocklist.
err := pa.checkHostLists(ident.Value)
if err != nil {
subErrors = append(subErrors, subError(ident, err))
continue
}
}
return combineSubErrors(subErrors)
}
// WellFormedIdentifiers returns an error if any of the provided domains do not
// meet these criteria:
// WellFormedIdentifiers returns an error if any of the provided identifiers do
// not meet these criteria:
//
// For DNS identifiers:
// - MUST contains only lowercase characters, numbers, hyphens, and dots
// - MUST NOT have more than maxLabels labels
// - MUST follow the DNS hostname syntax rules in RFC 1035 and RFC 2181
//
// In particular, it:
// In particular, DNS identifiers:
// - MUST NOT contain underscores
// - MUST NOT match the syntax of an IP address
// - MUST end in a public suffix
@ -433,25 +482,33 @@ func (pa *AuthorityImpl) WillingToIssue(idents identifier.ACMEIdentifiers) error
// - MUST NOT be a label-wise suffix match for a name on the block list,
// where comparison is case-independent (normalized to lower case)
//
// If a domain contains a *, we additionally require:
// If a DNS identifier contains a *, we additionally require:
// - There is at most one `*` wildcard character
// - That the wildcard character is the leftmost label
// - That the wildcard label is not immediately adjacent to a top level ICANN
// TLD
//
// If multiple domains are invalid, the error will contain suberrors specific to
// each domain.
// For IP identifiers:
// - MUST match the syntax of an IP address
// - MUST NOT be in an IANA special-purpose address registry
//
// If multiple identifiers are invalid, the error will contain suberrors
// specific to each identifier.
func WellFormedIdentifiers(idents identifier.ACMEIdentifiers) error {
var subErrors []berrors.SubBoulderError
for _, ident := range idents {
// TODO(#7311): When this gets a third case for TypeIP, this will be
// more elegant as a switch/case.
if ident.Type == identifier.TypeDNS {
switch ident.Type {
case identifier.TypeDNS:
err := ValidDomain(ident.Value)
if err != nil {
subErrors = append(subErrors, subError(ident, err))
}
} else {
case identifier.TypeIP:
err := validIP(ident.Value)
if err != nil {
subErrors = append(subErrors, subError(ident, err))
}
default:
subErrors = append(subErrors, subError(ident, errUnsupportedIdent))
}
}
@ -530,26 +587,34 @@ func (pa *AuthorityImpl) checkHostLists(domain string) error {
// filtering can happen dynamically at request rather than being set in stone
// at creation time.
func (pa *AuthorityImpl) ChallengeTypesFor(ident identifier.ACMEIdentifier) ([]core.AcmeChallenge, error) {
// If the identifier is for a DNS wildcard name we only provide a DNS-01
// challenge, to comply with the BRs Sections 3.2.2.4.19 and 3.2.2.4.20
// stating that ACME HTTP-01 and TLS-ALPN-01 are not suitable for validating
// Wildcard Domains.
if ident.Type == identifier.TypeDNS && strings.HasPrefix(ident.Value, "*.") {
return []core.AcmeChallenge{core.ChallengeTypeDNS01}, nil
}
switch ident.Type {
case identifier.TypeDNS:
// If the identifier is for a DNS wildcard name we only provide a DNS-01
// challenge, to comply with the BRs Sections 3.2.2.4.19 and 3.2.2.4.20
// stating that ACME HTTP-01 and TLS-ALPN-01 are not suitable for validating
// Wildcard Domains.
if strings.HasPrefix(ident.Value, "*.") {
return []core.AcmeChallenge{core.ChallengeTypeDNS01}, nil
}
// Return all challenge types we support for non-wildcard DNS identifiers.
if ident.Type == identifier.TypeDNS {
// Return all challenge types we support for non-wildcard DNS identifiers.
return []core.AcmeChallenge{
core.ChallengeTypeHTTP01,
core.ChallengeTypeDNS01,
core.ChallengeTypeTLSALPN01,
}, nil
case identifier.TypeIP:
// Only HTTP-01 and TLS-ALPN-01 are suitable for IP address identifiers
// per RFC 8738, Sec. 4.
return []core.AcmeChallenge{
core.ChallengeTypeHTTP01,
core.ChallengeTypeTLSALPN01,
}, nil
default:
// Otherwise return an error because we don't support any challenges for this
// identifier type.
return nil, fmt.Errorf("unrecognized identifier type %q", ident.Type)
}
// Otherwise return an error because we don't support any challenges for this
// identifier type.
return nil, fmt.Errorf("unrecognized identifier type %q", ident.Type)
}
// ChallengeTypeEnabled returns whether the specified challenge type is enabled
@ -583,3 +648,10 @@ func (pa *AuthorityImpl) CheckAuthzChallenges(authz *core.Authorization) error {
return nil
}
// IdentifierTypeEnabled returns whether the specified identifier type is enabled
func (pa *AuthorityImpl) IdentifierTypeEnabled(t identifier.IdentifierType) bool {
pa.blocklistMu.RLock()
defer pa.blocklistMu.RUnlock()
return pa.enabledIdentifiers[t]
}

View File

@ -4,6 +4,7 @@ import (
"fmt"
"net/netip"
"os"
"strings"
"testing"
"gopkg.in/yaml.v3"
@ -23,7 +24,12 @@ func paImpl(t *testing.T) *AuthorityImpl {
core.ChallengeTypeTLSALPN01: true,
}
pa, err := New(enabledChallenges, blog.NewMock())
enabledIdentifiers := map[identifier.IdentifierType]bool{
identifier.TypeDNS: true,
identifier.TypeIP: true,
}
pa, err := New(enabledIdentifiers, enabledChallenges, blog.NewMock())
if err != nil {
t.Fatalf("Couldn't create policy implementation: %s", err)
}
@ -32,110 +38,146 @@ func paImpl(t *testing.T) *AuthorityImpl {
func TestWellFormedIdentifiers(t *testing.T) {
testCases := []struct {
domain string
err error
ident identifier.ACMEIdentifier
err error
}{
{``, errEmptyName}, // Empty name
{`zomb!.com`, errInvalidDNSCharacter}, // ASCII character out of range
{`emailaddress@myseriously.present.com`, errInvalidDNSCharacter},
{`user:pass@myseriously.present.com`, errInvalidDNSCharacter},
{`zömbo.com`, errInvalidDNSCharacter}, // non-ASCII character
{`127.0.0.1`, errIPAddress}, // IPv4 address
{`fe80::1:1`, errInvalidDNSCharacter}, // IPv6 addresses
{`[2001:db8:85a3:8d3:1319:8a2e:370:7348]`, errInvalidDNSCharacter}, // unexpected IPv6 variants
{`[2001:db8:85a3:8d3:1319:8a2e:370:7348]:443`, errInvalidDNSCharacter},
{`2001:db8::/32`, errInvalidDNSCharacter},
{`a.b.c.d.e.f.g.h.i.j.k`, errTooManyLabels}, // Too many labels (>10)
// Invalid identifier types
{identifier.ACMEIdentifier{}, errUnsupportedIdent}, // Empty identifier type
{identifier.ACMEIdentifier{Type: "fnord", Value: "uh-oh, Spaghetti-Os[tm]"}, errUnsupportedIdent},
{`www.0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef012345.com`, errNameTooLong}, // Too long (254 characters)
// Empty identifier values
{identifier.NewDNS(``), errEmptyIdentifier}, // Empty DNS identifier
{identifier.ACMEIdentifier{Type: "ip"}, errEmptyIdentifier}, // Empty IP identifier
{`www.ef0123456789abcdef013456789abcdef012345.789abcdef012345679abcdef0123456789abcdef01234.6789abcdef0123456789abcdef0.23456789abcdef0123456789a.cdef0123456789abcdef0123456789ab.def0123456789abcdef0123456789.bcdef0123456789abcdef012345.com`, nil}, // OK, not too long (240 characters)
// DNS follies
{`www.abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz.com`, errLabelTooLong}, // Label too long (>63 characters)
{identifier.NewDNS(`zomb!.com`), errInvalidDNSCharacter}, // ASCII character out of range
{identifier.NewDNS(`emailaddress@myseriously.present.com`), errInvalidDNSCharacter},
{identifier.NewDNS(`user:pass@myseriously.present.com`), errInvalidDNSCharacter},
{identifier.NewDNS(`zömbo.com`), errInvalidDNSCharacter}, // non-ASCII character
{identifier.NewDNS(`127.0.0.1`), errIPAddressInDNS}, // IPv4 address
{identifier.NewDNS(`fe80::1:1`), errInvalidDNSCharacter}, // IPv6 address
{identifier.NewDNS(`[2001:db8:85a3:8d3:1319:8a2e:370:7348]`), errInvalidDNSCharacter}, // unexpected IPv6 variants
{identifier.NewDNS(`[2001:db8:85a3:8d3:1319:8a2e:370:7348]:443`), errInvalidDNSCharacter},
{identifier.NewDNS(`2001:db8::/32`), errInvalidDNSCharacter},
{identifier.NewDNS(`a.b.c.d.e.f.g.h.i.j.k`), errTooManyLabels}, // Too many labels (>10)
{`www.-ombo.com`, errInvalidDNSCharacter}, // Label starts with '-'
{`www.zomb-.com`, errInvalidDNSCharacter}, // Label ends with '-'
{`xn--.net`, errInvalidDNSCharacter}, // Label ends with '-'
{`-0b.net`, errInvalidDNSCharacter}, // First label begins with '-'
{`-0.net`, errInvalidDNSCharacter}, // First label begins with '-'
{`-.net`, errInvalidDNSCharacter}, // First label is only '-'
{`---.net`, errInvalidDNSCharacter}, // First label is only hyphens
{`0`, errTooFewLabels},
{`1`, errTooFewLabels},
{`*`, errMalformedWildcard},
{`**`, errTooManyWildcards},
{`*.*`, errTooManyWildcards},
{`zombo*com`, errMalformedWildcard},
{`*.com`, errICANNTLDWildcard},
{`..a`, errLabelTooShort},
{`a..a`, errLabelTooShort},
{`.a..a`, errLabelTooShort},
{`..foo.com`, errLabelTooShort},
{`.`, errNameEndsInDot},
{`..`, errNameEndsInDot},
{`a..`, errNameEndsInDot},
{`.....`, errNameEndsInDot},
{`.a.`, errNameEndsInDot},
{`www.zombo.com.`, errNameEndsInDot},
{`www.zombo_com.com`, errInvalidDNSCharacter},
{`\uFEFF`, errInvalidDNSCharacter}, // Byte order mark
{`\uFEFFwww.zombo.com`, errInvalidDNSCharacter},
{`www.zom\u202Ebo.com`, errInvalidDNSCharacter}, // Right-to-Left Override
{`\u202Ewww.zombo.com`, errInvalidDNSCharacter},
{`www.zom\u200Fbo.com`, errInvalidDNSCharacter}, // Right-to-Left Mark
{`\u200Fwww.zombo.com`, errInvalidDNSCharacter},
{identifier.NewDNS(`www.0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef012345.com`), errNameTooLong}, // Too long (254 characters)
{identifier.NewDNS(`www.ef0123456789abcdef013456789abcdef012345.789abcdef012345679abcdef0123456789abcdef01234.6789abcdef0123456789abcdef0.23456789abcdef0123456789a.cdef0123456789abcdef0123456789ab.def0123456789abcdef0123456789.bcdef0123456789abcdef012345.com`), nil}, // OK, not too long (240 characters)
{identifier.NewDNS(`www.abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz.com`), errLabelTooLong}, // Label too long (>63 characters)
{identifier.NewDNS(`www.-ombo.com`), errInvalidDNSCharacter}, // Label starts with '-'
{identifier.NewDNS(`www.zomb-.com`), errInvalidDNSCharacter}, // Label ends with '-'
{identifier.NewDNS(`xn--.net`), errInvalidDNSCharacter}, // Label ends with '-'
{identifier.NewDNS(`-0b.net`), errInvalidDNSCharacter}, // First label begins with '-'
{identifier.NewDNS(`-0.net`), errInvalidDNSCharacter}, // First label begins with '-'
{identifier.NewDNS(`-.net`), errInvalidDNSCharacter}, // First label is only '-'
{identifier.NewDNS(`---.net`), errInvalidDNSCharacter}, // First label is only hyphens
{identifier.NewDNS(`0`), errTooFewLabels},
{identifier.NewDNS(`1`), errTooFewLabels},
{identifier.NewDNS(`*`), errMalformedWildcard},
{identifier.NewDNS(`**`), errTooManyWildcards},
{identifier.NewDNS(`*.*`), errTooManyWildcards},
{identifier.NewDNS(`zombo*com`), errMalformedWildcard},
{identifier.NewDNS(`*.com`), errICANNTLDWildcard},
{identifier.NewDNS(`..a`), errLabelTooShort},
{identifier.NewDNS(`a..a`), errLabelTooShort},
{identifier.NewDNS(`.a..a`), errLabelTooShort},
{identifier.NewDNS(`..foo.com`), errLabelTooShort},
{identifier.NewDNS(`.`), errNameEndsInDot},
{identifier.NewDNS(`..`), errNameEndsInDot},
{identifier.NewDNS(`a..`), errNameEndsInDot},
{identifier.NewDNS(`.....`), errNameEndsInDot},
{identifier.NewDNS(`.a.`), errNameEndsInDot},
{identifier.NewDNS(`www.zombo.com.`), errNameEndsInDot},
{identifier.NewDNS(`www.zombo_com.com`), errInvalidDNSCharacter},
{identifier.NewDNS(`\uFEFF`), errInvalidDNSCharacter}, // Byte order mark
{identifier.NewDNS(`\uFEFFwww.zombo.com`), errInvalidDNSCharacter},
{identifier.NewDNS(`www.zom\u202Ebo.com`), errInvalidDNSCharacter}, // Right-to-Left Override
{identifier.NewDNS(`\u202Ewww.zombo.com`), errInvalidDNSCharacter},
{identifier.NewDNS(`www.zom\u200Fbo.com`), errInvalidDNSCharacter}, // Right-to-Left Mark
{identifier.NewDNS(`\u200Fwww.zombo.com`), errInvalidDNSCharacter},
// Underscores are technically disallowed in DNS. Some DNS
// implementations accept them but we will be conservative.
{`www.zom_bo.com`, errInvalidDNSCharacter},
{`zombocom`, errTooFewLabels},
{`localhost`, errTooFewLabels},
{`mail`, errTooFewLabels},
{identifier.NewDNS(`www.zom_bo.com`), errInvalidDNSCharacter},
{identifier.NewDNS(`zombocom`), errTooFewLabels},
{identifier.NewDNS(`localhost`), errTooFewLabels},
{identifier.NewDNS(`mail`), errTooFewLabels},
// disallow capitalized letters for #927
{`CapitalizedLetters.com`, errInvalidDNSCharacter},
{identifier.NewDNS(`CapitalizedLetters.com`), errInvalidDNSCharacter},
{`example.acting`, errNonPublic},
{`example.internal`, errNonPublic},
{identifier.NewDNS(`example.acting`), errNonPublic},
{identifier.NewDNS(`example.internal`), errNonPublic},
// All-numeric final label not okay.
{`www.zombo.163`, errNonPublic},
{`xn--109-3veba6djs1bfxlfmx6c9g.xn--f1awi.xn--p1ai`, errMalformedIDN}, // Not in Unicode NFC
{`bq--abwhky3f6fxq.jakacomo.com`, errInvalidRLDH},
{identifier.NewDNS(`www.zombo.163`), errNonPublic},
{identifier.NewDNS(`xn--109-3veba6djs1bfxlfmx6c9g.xn--f1awi.xn--p1ai`), errMalformedIDN}, // Not in Unicode NFC
{identifier.NewDNS(`bq--abwhky3f6fxq.jakacomo.com`), errInvalidRLDH},
// Three hyphens starting at third second char of first label.
{`bq---abwhky3f6fxq.jakacomo.com`, errInvalidRLDH},
{identifier.NewDNS(`bq---abwhky3f6fxq.jakacomo.com`), errInvalidRLDH},
// Three hyphens starting at second char of first label.
{`h---test.hk2yz.org`, errInvalidRLDH},
{`co.uk`, errICANNTLD},
{`foo.bd`, errICANNTLD},
{identifier.NewDNS(`h---test.hk2yz.org`), errInvalidRLDH},
{identifier.NewDNS(`co.uk`), errICANNTLD},
{identifier.NewDNS(`foo.bd`), errICANNTLD},
// IP oopsies
{identifier.ACMEIdentifier{Type: "ip", Value: `zombo.com`}, errIPInvalid}, // That's DNS!
// Unexpected IPv4 variants
{identifier.ACMEIdentifier{Type: "ip", Value: `192.168.1.1.1`}, errIPInvalid}, // extra octet
{identifier.ACMEIdentifier{Type: "ip", Value: `192.168.1.256`}, errIPInvalid}, // octet out of range
{identifier.ACMEIdentifier{Type: "ip", Value: `192.168.1.a1`}, errIPInvalid}, // character out of range
{identifier.ACMEIdentifier{Type: "ip", Value: `192.168.1.0/24`}, errIPInvalid}, // with CIDR
{identifier.ACMEIdentifier{Type: "ip", Value: `192.168.1.1:443`}, errIPInvalid}, // with port
{identifier.ACMEIdentifier{Type: "ip", Value: `0xc0a80101`}, errIPInvalid}, // as hex
{identifier.ACMEIdentifier{Type: "ip", Value: `1.1.168.192.in-addr.arpa`}, errIPInvalid}, // reverse DNS
// Unexpected IPv6 variants
{identifier.ACMEIdentifier{Type: "ip", Value: `3fff:aaa:a:c0ff:ee:a:bad:deed:ffff`}, errIPInvalid}, // extra octet
{identifier.ACMEIdentifier{Type: "ip", Value: `3fff:aaa:a:c0ff:ee:a:bad:mead`}, errIPInvalid}, // character out of range
{identifier.ACMEIdentifier{Type: "ip", Value: `2001:db8::/32`}, errIPInvalid}, // with CIDR
{identifier.ACMEIdentifier{Type: "ip", Value: `[3fff:aaa:a:c0ff:ee:a:bad:deed]`}, errIPInvalid}, // in brackets
{identifier.ACMEIdentifier{Type: "ip", Value: `[3fff:aaa:a:c0ff:ee:a:bad:deed]:443`}, errIPInvalid}, // in brackets, with port
{identifier.ACMEIdentifier{Type: "ip", Value: `0x3fff0aaa000ac0ff00ee000a0baddeed`}, errIPInvalid}, // as hex
{identifier.ACMEIdentifier{Type: "ip", Value: `d.e.e.d.d.a.b.0.a.0.0.0.e.e.0.0.f.f.0.c.a.0.0.0.a.a.a.0.f.f.f.3.ip6.arpa`}, errIPInvalid}, // reverse DNS
{identifier.ACMEIdentifier{Type: "ip", Value: `3fff:0aaa:a:c0ff:ee:a:bad:deed`}, errIPInvalid}, // leading 0 in 2nd octet (RFC 5952, Sec. 4.1)
{identifier.ACMEIdentifier{Type: "ip", Value: `3fff:aaa:0:0:0:a:bad:deed`}, errIPInvalid}, // lone 0s in 3rd-5th octets, :: not used (RFC 5952, Sec. 4.2.1)
{identifier.ACMEIdentifier{Type: "ip", Value: `3fff:aaa::c0ff:ee:a:bad:deed`}, errIPInvalid}, // :: used for just one empty octet (RFC 5952, Sec. 4.2.2)
{identifier.ACMEIdentifier{Type: "ip", Value: `3fff:aaa::ee:0:0:0`}, errIPInvalid}, // :: used for the shorter of two possible collapses (RFC 5952, Sec. 4.2.3)
{identifier.ACMEIdentifier{Type: "ip", Value: `fe80:0:0:0:a::`}, errIPInvalid}, // :: used for the last of two possible equal-length collapses (RFC 5952, Sec. 4.2.3)
{identifier.ACMEIdentifier{Type: "ip", Value: `3fff:aaa:a:C0FF:EE:a:bad:deed`}, errIPInvalid}, // alpha characters capitalized (RFC 5952, Sec. 4.3)
{identifier.ACMEIdentifier{Type: "ip", Value: `::ffff:192.168.1.1`}, errIPInvalid}, // IPv6-encapsulated IPv4
// IANA special-purpose address blocks
{identifier.NewIP(netip.MustParseAddr("192.0.2.129")), errIPSpecialPurpose}, // Documentation (TEST-NET-1)
{identifier.NewIP(netip.MustParseAddr("2001:db8:eee:eeee:eeee:eeee:d01:f1")), errIPSpecialPurpose}, // Documentation
}
// Test syntax errors
for _, tc := range testCases {
err := WellFormedIdentifiers(identifier.ACMEIdentifiers{identifier.NewDNS(tc.domain)})
err := WellFormedIdentifiers(identifier.ACMEIdentifiers{tc.ident})
if tc.err == nil {
test.AssertNil(t, err, fmt.Sprintf("Unexpected error for domain %q, got %s", tc.domain, err))
test.AssertNil(t, err, fmt.Sprintf("Unexpected error for %q identifier %q, got %s", tc.ident.Type, tc.ident.Value, err))
} else {
test.AssertError(t, err, fmt.Sprintf("Expected error for domain %q, but got none", tc.domain))
test.AssertError(t, err, fmt.Sprintf("Expected error for %q identifier %q, but got none", tc.ident.Type, tc.ident.Value))
var berr *berrors.BoulderError
test.AssertErrorWraps(t, err, &berr)
test.AssertContains(t, berr.Error(), tc.err.Error())
}
}
err := WellFormedIdentifiers(identifier.ACMEIdentifiers{identifier.NewIP(netip.MustParseAddr("9.9.9.9"))})
test.AssertError(t, err, "Expected error for IP, but got none")
var berr *berrors.BoulderError
test.AssertErrorWraps(t, err, &berr)
test.AssertContains(t, berr.Error(), errUnsupportedIdent.Error())
}
func TestWillingToIssue(t *testing.T) {
shouldBeBlocked := []string{
`highvalue.website1.org`,
`website2.co.uk`,
`www.website3.com`,
`lots.of.labels.website4.com`,
`banned.in.dc.com`,
`bad.brains.banned.in.dc.com`,
shouldBeBlocked := identifier.ACMEIdentifiers{
identifier.NewDNS(`highvalue.website1.org`),
identifier.NewDNS(`website2.co.uk`),
identifier.NewDNS(`www.website3.com`),
identifier.NewDNS(`lots.of.labels.website4.com`),
identifier.NewDNS(`banned.in.dc.com`),
identifier.NewDNS(`bad.brains.banned.in.dc.com`),
}
blocklistContents := []string{
`website2.com`,
@ -153,15 +195,17 @@ func TestWillingToIssue(t *testing.T) {
`banned.in.dc.com`,
}
shouldBeAccepted := []string{
`lowvalue.website1.org`,
`website4.sucks`,
"www.unrelated.com",
"unrelated.com",
"www.8675309.com",
"8675309.com",
"web5ite2.com",
"www.web-site2.com",
shouldBeAccepted := identifier.ACMEIdentifiers{
identifier.NewDNS(`lowvalue.website1.org`),
identifier.NewDNS(`website4.sucks`),
identifier.NewDNS(`www.unrelated.com`),
identifier.NewDNS(`unrelated.com`),
identifier.NewDNS(`www.8675309.com`),
identifier.NewDNS(`8675309.com`),
identifier.NewDNS(`web5ite2.com`),
identifier.NewDNS(`www.web-site2.com`),
identifier.NewIP(netip.MustParseAddr(`9.9.9.9`)),
identifier.NewIP(netip.MustParseAddr(`2620:fe::fe`)),
}
policy := blockedNamesPolicy{
@ -186,8 +230,8 @@ func TestWillingToIssue(t *testing.T) {
err = pa.WillingToIssue(identifier.ACMEIdentifiers{identifier.NewDNS("www.xn--m.com")})
test.AssertError(t, err, "WillingToIssue didn't fail on a malformed IDN")
// Invalid identifier type
err = pa.WillingToIssue(identifier.ACMEIdentifiers{identifier.NewIP(netip.MustParseAddr("1.1.1.1"))})
test.AssertError(t, err, "WillingToIssue didn't fail on an IP address")
err = pa.WillingToIssue(identifier.ACMEIdentifiers{identifier.ACMEIdentifier{Type: "fnord", Value: "uh-oh, Spaghetti-Os[tm]"}})
test.AssertError(t, err, "WillingToIssue didn't fail on an invalid identifier type")
// Valid encoding
err = pa.WillingToIssue(identifier.ACMEIdentifiers{identifier.NewDNS("www.xn--mnich-kva.com")})
test.AssertNotError(t, err, "WillingToIssue failed on a properly formed IDN")
@ -197,18 +241,18 @@ func TestWillingToIssue(t *testing.T) {
features.Reset()
// Test expected blocked domains
for _, domain := range shouldBeBlocked {
err := pa.WillingToIssue(identifier.ACMEIdentifiers{identifier.NewDNS(domain)})
test.AssertError(t, err, "domain was not correctly forbidden")
for _, ident := range shouldBeBlocked {
err := pa.WillingToIssue(identifier.ACMEIdentifiers{ident})
test.AssertError(t, err, "identifier was not correctly forbidden")
var berr *berrors.BoulderError
test.AssertErrorWraps(t, err, &berr)
test.AssertContains(t, berr.Detail, errPolicyForbidden.Error())
}
// Test acceptance of good names
for _, domain := range shouldBeAccepted {
err := pa.WillingToIssue(identifier.ACMEIdentifiers{identifier.NewDNS(domain)})
test.AssertNotError(t, err, "domain was incorrectly forbidden")
for _, ident := range shouldBeAccepted {
err := pa.WillingToIssue(identifier.ACMEIdentifiers{ident})
test.AssertNotError(t, err, "identifier was incorrectly forbidden")
}
}
@ -415,15 +459,22 @@ func TestChallengeTypesFor(t *testing.T) {
},
},
{
name: "wildcard",
name: "dns wildcard",
ident: identifier.NewDNS("*.example.com"),
wantChalls: []core.AcmeChallenge{
core.ChallengeTypeDNS01,
},
},
{
name: "other",
ident: identifier.NewIP(netip.MustParseAddr("1.2.3.4")),
name: "ip",
ident: identifier.NewIP(netip.MustParseAddr("1.2.3.4")),
wantChalls: []core.AcmeChallenge{
core.ChallengeTypeHTTP01, core.ChallengeTypeTLSALPN01,
},
},
{
name: "invalid",
ident: identifier.ACMEIdentifier{Type: "fnord", Value: "uh-oh, Spaghetti-Os[tm]"},
wantErr: "unrecognized identifier type",
},
}
@ -573,3 +624,109 @@ func TestCheckAuthzChallenges(t *testing.T) {
})
}
}
func TestWillingToIssue_IdentifierType(t *testing.T) {
t.Parallel()
testCases := []struct {
name string
ident identifier.ACMEIdentifier
enabled map[identifier.IdentifierType]bool
wantErr string
}{
{
name: "DNS identifier, none enabled",
ident: identifier.NewDNS("example.com"),
enabled: nil,
wantErr: "The ACME server has disabled this identifier type",
},
{
name: "DNS identifier, DNS enabled",
ident: identifier.NewDNS("example.com"),
enabled: map[identifier.IdentifierType]bool{identifier.TypeDNS: true},
wantErr: "",
},
{
name: "DNS identifier, DNS & IP enabled",
ident: identifier.NewDNS("example.com"),
enabled: map[identifier.IdentifierType]bool{identifier.TypeDNS: true, identifier.TypeIP: true},
wantErr: "",
},
{
name: "DNS identifier, IP enabled",
ident: identifier.NewDNS("example.com"),
enabled: map[identifier.IdentifierType]bool{identifier.TypeIP: true},
wantErr: "The ACME server has disabled this identifier type",
},
{
name: "IP identifier, none enabled",
ident: identifier.NewIP(netip.MustParseAddr("9.9.9.9")),
enabled: nil,
wantErr: "The ACME server has disabled this identifier type",
},
{
name: "IP identifier, DNS enabled",
ident: identifier.NewIP(netip.MustParseAddr("9.9.9.9")),
enabled: map[identifier.IdentifierType]bool{identifier.TypeDNS: true},
wantErr: "The ACME server has disabled this identifier type",
},
{
name: "IP identifier, DNS & IP enabled",
ident: identifier.NewIP(netip.MustParseAddr("9.9.9.9")),
enabled: map[identifier.IdentifierType]bool{identifier.TypeDNS: true, identifier.TypeIP: true},
wantErr: "",
},
{
name: "IP identifier, IP enabled",
ident: identifier.NewIP(netip.MustParseAddr("9.9.9.9")),
enabled: map[identifier.IdentifierType]bool{identifier.TypeIP: true},
wantErr: "",
},
{
name: "invalid identifier type",
ident: identifier.ACMEIdentifier{Type: "drywall", Value: "oh yeah!"},
enabled: map[identifier.IdentifierType]bool{"drywall": true},
wantErr: "Invalid identifier type",
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
policy := blockedNamesPolicy{
HighRiskBlockedNames: []string{"zombo.gov.us"},
ExactBlockedNames: []string{`highvalue.website1.org`},
AdminBlockedNames: []string{`banned.in.dc.com`},
}
yamlPolicyBytes, err := yaml.Marshal(policy)
test.AssertNotError(t, err, "Couldn't YAML serialize blocklist")
yamlPolicyFile, _ := os.CreateTemp("", "test-blocklist.*.yaml")
defer os.Remove(yamlPolicyFile.Name())
err = os.WriteFile(yamlPolicyFile.Name(), yamlPolicyBytes, 0640)
test.AssertNotError(t, err, "Couldn't write YAML blocklist")
pa := paImpl(t)
err = pa.LoadHostnamePolicyFile(yamlPolicyFile.Name())
test.AssertNotError(t, err, "Couldn't load rules")
pa.enabledIdentifiers = tc.enabled
err = pa.WillingToIssue(identifier.ACMEIdentifiers{tc.ident})
if tc.wantErr == "" {
if err != nil {
t.Errorf("should have succeeded, but got error: %s", err.Error())
}
} else {
if err == nil {
t.Errorf("should have failed")
} else if !strings.Contains(err.Error(), tc.wantErr) {
t.Errorf("wrong error; wanted '%s', but got '%s'", tc.wantErr, err.Error())
}
}
})
}
}

View File

@ -301,10 +301,16 @@ func initAuthorities(t *testing.T) (*DummyValidationAuthority, sapb.StorageAutho
}
va := va.RemoteClients{VAClient: dummyVA, CAAClient: dummyVA}
pa, err := policy.New(map[core.AcmeChallenge]bool{
core.ChallengeTypeHTTP01: true,
core.ChallengeTypeDNS01: true,
}, blog.NewMock())
pa, err := policy.New(
map[identifier.IdentifierType]bool{
identifier.TypeDNS: true,
identifier.TypeIP: true,
},
map[core.AcmeChallenge]bool{
core.ChallengeTypeHTTP01: true,
core.ChallengeTypeDNS01: true,
},
blog.NewMock())
test.AssertNotError(t, err, "Couldn't create PA")
err = pa.LoadHostnamePolicyFile("../test/hostname-policy.yaml")
test.AssertNotError(t, err, "Couldn't set hostname policy")
@ -2795,10 +2801,16 @@ func TestFinalizeOrderDisabledChallenge(t *testing.T) {
test.AssertNotError(t, err, "Error creating policy forbid CSR")
// Replace the Policy Authority with one which has this challenge type disabled
pa, err := policy.New(map[core.AcmeChallenge]bool{
core.ChallengeTypeDNS01: true,
core.ChallengeTypeTLSALPN01: true,
}, ra.log)
pa, err := policy.New(
map[identifier.IdentifierType]bool{
identifier.TypeDNS: true,
identifier.TypeIP: true,
},
map[core.AcmeChallenge]bool{
core.ChallengeTypeDNS01: true,
core.ChallengeTypeTLSALPN01: true,
},
ra.log)
test.AssertNotError(t, err, "creating test PA")
err = pa.LoadHostnamePolicyFile("../test/hostname-policy.yaml")
test.AssertNotError(t, err, "loading test hostname policy")
@ -3199,7 +3211,7 @@ func TestUpdateMissingAuthorization(t *testing.T) {
func TestPerformValidationBadChallengeType(t *testing.T) {
_, _, ra, _, fc, cleanUp := initAuthorities(t)
defer cleanUp()
pa, err := policy.New(map[core.AcmeChallenge]bool{}, blog.NewMock())
pa, err := policy.New(map[identifier.IdentifierType]bool{}, map[core.AcmeChallenge]bool{}, blog.NewMock())
test.AssertNotError(t, err, "Couldn't create PA")
ra.PA = pa
@ -4091,10 +4103,16 @@ func TestGetAuthorization(t *testing.T) {
}
// With HTTP01 enabled, GetAuthorization should pass the mock challenge through.
pa, err := policy.New(map[core.AcmeChallenge]bool{
core.ChallengeTypeHTTP01: true,
core.ChallengeTypeDNS01: true,
}, blog.NewMock())
pa, err := policy.New(
map[identifier.IdentifierType]bool{
identifier.TypeDNS: true,
identifier.TypeIP: true,
},
map[core.AcmeChallenge]bool{
core.ChallengeTypeHTTP01: true,
core.ChallengeTypeDNS01: true,
},
blog.NewMock())
test.AssertNotError(t, err, "Couldn't create PA")
ra.PA = pa
authz, err := ra.GetAuthorization(context.Background(), &rapb.GetAuthorizationRequest{Id: 1})
@ -4103,9 +4121,15 @@ func TestGetAuthorization(t *testing.T) {
test.AssertEquals(t, authz.Challenges[0].Type, string(core.ChallengeTypeHTTP01))
// With HTTP01 disabled, GetAuthorization should filter out the mock challenge.
pa, err = policy.New(map[core.AcmeChallenge]bool{
core.ChallengeTypeDNS01: true,
}, blog.NewMock())
pa, err = policy.New(
map[identifier.IdentifierType]bool{
identifier.TypeDNS: true,
identifier.TypeIP: true,
},
map[core.AcmeChallenge]bool{
core.ChallengeTypeDNS01: true,
},
blog.NewMock())
test.AssertNotError(t, err, "Couldn't create PA")
ra.PA = pa
authz, err = ra.GetAuthorization(context.Background(), &rapb.GetAuthorizationRequest{Id: 1})

View File

@ -205,7 +205,7 @@ func TestValidateIdForName(t *testing.T) {
limit: CertificatesPerDomain,
desc: "empty domain",
id: "",
err: "name is empty",
err: "Identifier value (name) is empty",
},
{
limit: CertificatesPerFQDNSet,

View File

@ -190,6 +190,10 @@
"http-01": true,
"dns-01": true,
"tls-alpn-01": true
},
"identifiers": {
"dns": true,
"ip": true
}
},
"syslog": {

View File

@ -29,6 +29,10 @@
"http-01": true,
"dns-01": true,
"tls-alpn-01": true
},
"identifiers": {
"dns": true,
"ip": true
}
},
"syslog": {

View File

@ -181,6 +181,10 @@
"http-01": true,
"dns-01": true,
"tls-alpn-01": true
},
"identifiers": {
"dns": true,
"ip": true
}
},
"syslog": {