PA: Support YAML for hostname policy. (#4180)

Our existing hostname policy configs use JSON. We would like to switch to YAML
to match the rate limit policy configs and to support commenting/tagging
entries in the hostname policy.

The PA is updated to support both JSON and YAML while we migrate the existing
policy data to the new format. To verify there are no changes in functionality
the existing unit tests were updated to test the same policy expressed in YAML
and JSON form.

In integration tests the `config` tree continues to use a JSON hostname policy
file to test the legacy support. In `config-next` a YAML hostname policy file
is used instead.

The new YAML format allows separating out `HighRiskBlockedNames` (primarily
used to meet the requirement of managing issuance for "high risk" domains per
CABF BRs, mostly static) and `AdminBlockedNames` (used after administrative
action is taken to block a domain. Additions are made with some frequency).
Since the rate at which we change entries in these lists differs it is helpful
to separate them in the policy content.
This commit is contained in:
Daniel McCarney 2019-04-26 14:35:28 -04:00 committed by GitHub
parent e49ffaf94c
commit 748f315b1a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 281 additions and 144 deletions

View File

@ -20,16 +20,17 @@ import (
"github.com/letsencrypt/boulder/iana"
blog "github.com/letsencrypt/boulder/log"
"github.com/letsencrypt/boulder/reloader"
"gopkg.in/yaml.v2"
)
// AuthorityImpl enforces CA policy decisions.
type AuthorityImpl struct {
log blog.Logger
blacklist map[string]bool
exactBlacklist map[string]bool
wildcardExactBlacklist map[string]bool
blacklistMu sync.RWMutex
blocklist map[string]bool
exactBlocklist map[string]bool
wildcardExactBlocklist map[string]bool
blocklistMu sync.RWMutex
enabledChallenges map[string]bool
pseudoRNG *rand.Rand
@ -49,46 +50,106 @@ func New(challengeTypes map[string]bool) (*AuthorityImpl, error) {
return &pa, nil
}
type blacklistJSON struct {
Blacklist []string
ExactBlacklist []string
// blockedNamesPolicy is a struct holding lists of blocked domain names. One for
// exact blocks and one for blocks including all subdomains.
type blockedNamesPolicy struct {
// ExactBlockedNames is a list of domain names. Issuance for names exactly
// matching an entry in the list will be forbidden. (e.g. `ExactBlockedNames`
// containing `www.example.com` will not block `example.com` or
// `mail.example.com`).
//
// TODO(@cpu): Remove the JSON tag when data is updated to use the new field names
ExactBlockedNames []string `json:"exactBlacklist" yaml:"ExactBlockedNames"`
// HighRiskBlockedNames is like ExactBlockedNames except that issuance is
// blocked for subdomains as well. (e.g. BlockedNames containing `example.com`
// will block `www.example.com`).
//
// This list typically doesn't change with much regularity.
//
// TODO(@cpu): Remove the JSON tag when data is updated to use the new field names
HighRiskBlockedNames []string `json:"blacklist" yaml:"HighRiskBlockedNames"`
// AdminBlockedNames operates the same as BlockedNames but is changed with more
// frequency based on administrative blocks/revocations that are added over
// time above and beyond the high-risk domains. Managing these entries separately
// from HighRiskBlockedNames makes it easier to vet changes accurately.
AdminBlockedNames []string `yaml:"AdminBlockedNames"`
}
// SetHostnamePolicyFile will load the given policy file, returning error if it
// fails. It will also start a reloader in case the file changes.
// fails. It will also start a reloader in case the file changes. It supports
// YAML and JSON serialization formats and chooses the correct unserialization
// method based on the file extension which must be ".yaml", ".yml", or ".json".
func (pa *AuthorityImpl) SetHostnamePolicyFile(f string) error {
_, err := reloader.New(f, pa.loadHostnamePolicy, pa.hostnamePolicyLoadError)
return err
var loadHandler func([]byte) error
if strings.HasSuffix(f, ".json") {
loadHandler = pa.loadHostnamePolicy(json.Unmarshal)
} else if strings.HasSuffix(f, ".yml") || strings.HasSuffix(f, ".yaml") {
loadHandler = pa.loadHostnamePolicy(yaml.Unmarshal)
} else {
return fmt.Errorf(
"Hostname policy file %q has unknown extension. Supported: .yml,.yaml,.json",
f)
}
if _, err := reloader.New(f, loadHandler, pa.hostnamePolicyLoadError); err != nil {
return err
}
return nil
}
func (pa *AuthorityImpl) hostnamePolicyLoadError(err error) {
pa.log.AuditErrf("error loading hostname policy: %s", err)
}
func (pa *AuthorityImpl) loadHostnamePolicy(b []byte) error {
hash := sha256.Sum256(b)
pa.log.Infof("loading hostname policy, sha256: %s", hex.EncodeToString(hash[:]))
var bl blacklistJSON
err := json.Unmarshal(b, &bl)
if err != nil {
return err
}
if len(bl.Blacklist) == 0 {
return fmt.Errorf("No entries in blacklist.")
// unmarshalHandler is a function type that abstracts away a choice between
// json.Unmarshal and yaml.Unmarshal, both of which take a byte slice, an
// interface to unmarshal to, and return an error.
type unmarshalHandler func([]byte, interface{}) error
// loadHostnamePolicy returns a reloader dataCallback function that uses the
// unmarshalHandler to load a hostname policy. The returned callback is suitable
// for use with reloader.New()
func (pa *AuthorityImpl) loadHostnamePolicy(unmarshal unmarshalHandler) func([]byte) error {
// Return a reloader dataCallback that uses the provided unmarshalHandler.
return func(contents []byte) error {
hash := sha256.Sum256(contents)
pa.log.Infof("loading hostname policy, sha256: %s", hex.EncodeToString(hash[:]))
var policy blockedNamesPolicy
err := unmarshal(contents, &policy)
if err != nil {
return err
}
if len(policy.HighRiskBlockedNames) == 0 {
return fmt.Errorf("No entries in HighRiskBlockedNames.")
}
if len(policy.ExactBlockedNames) == 0 {
return fmt.Errorf("No entries in ExactBlockedNames.")
}
return pa.processHostnamePolicy(policy)
}
}
// processHostnamePolicy handles loading a new blockedNamesPolicy into the PA.
// All of the policy.ExactBlockedNames will be added to the
// wildcardExactBlocklist by processHostnamePolicy to ensure that wildcards for
// exact blocked names entries are forbidden.
func (pa *AuthorityImpl) processHostnamePolicy(policy blockedNamesPolicy) error {
nameMap := make(map[string]bool)
for _, v := range bl.Blacklist {
for _, v := range policy.HighRiskBlockedNames {
nameMap[v] = true
}
for _, v := range policy.AdminBlockedNames {
nameMap[v] = true
}
exactNameMap := make(map[string]bool)
wildcardNameMap := make(map[string]bool)
for _, v := range bl.ExactBlacklist {
for _, v := range policy.ExactBlockedNames {
exactNameMap[v] = true
// Remove the leftmost label of the exact blacklist entry to make an exact
// wildcard blacklist entry that will prevent issuing a wildcard that would
// include the exact blacklist entry. e.g. if "highvalue.example.com" is on
// the exact blacklist we want "example.com" to be on the
// wildcardExactBlacklist so that "*.example.com" cannot be issued.
// Remove the leftmost label of the exact blocked names entry to make an exact
// wildcard block list entry that will prevent issuing a wildcard that would
// include the exact blocklist entry. e.g. if "highvalue.example.com" is on
// the exact blocklist we want "example.com" to be in the
// wildcardExactBlocklist so that "*.example.com" cannot be issued.
//
// First, split the domain into two parts: the first label and the rest of the domain.
parts := strings.SplitN(v, ".", 2)
@ -96,17 +157,17 @@ func (pa *AuthorityImpl) loadHostnamePolicy(b []byte) error {
// at least be a "something." and a TLD like "com"
if len(parts) < 2 {
return fmt.Errorf(
"Malformed exact blacklist entry, only one label: %q", v)
"Malformed ExactBlockedNames entry, only one label: %q", v)
}
// Add the second part, the domain minus the first label, to the
// wildcardNameMap to block issuance for `*.`+parts[1]
wildcardNameMap[parts[1]] = true
}
pa.blacklistMu.Lock()
pa.blacklist = nameMap
pa.exactBlacklist = exactNameMap
pa.wildcardExactBlacklist = wildcardNameMap
pa.blacklistMu.Unlock()
pa.blocklistMu.Lock()
pa.blocklist = nameMap
pa.exactBlocklist = exactNameMap
pa.wildcardExactBlocklist = wildcardNameMap
pa.blocklistMu.Unlock()
return nil
}
@ -141,7 +202,7 @@ var (
errInvalidIdentifier = berrors.MalformedError("Invalid identifier type")
errNonPublic = berrors.MalformedError("Name does not end in a public suffix")
errICANNTLD = berrors.MalformedError("Name is an ICANN TLD")
errBlacklisted = berrors.RejectedIdentifierError("Policy forbids issuing for name")
errPolicyForbidden = berrors.RejectedIdentifierError("Policy forbids issuing for name")
errInvalidDNSCharacter = berrors.MalformedError("Invalid character in DNS name")
errNameTooLong = berrors.MalformedError("DNS name too long")
errIPAddress = berrors.MalformedError("Issuance for IP addresses not supported")
@ -174,7 +235,7 @@ var (
// * MUST NOT match the syntax of an IP address
// * MUST end in a public suffix
// * MUST have at least one label in addition to the public suffix
// * MUST NOT be a label-wise suffix match for a name on the black list,
// * 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 WillingToIssue returns an error, it will be of type MalformedRequestError
@ -260,7 +321,7 @@ func (pa *AuthorityImpl) WillingToIssue(id core.AcmeIdentifier) error {
return errICANNTLD
}
// Require no match against blacklist
// Require no match against hostname block lists
if err := pa.checkHostLists(domain); err != nil {
return err
}
@ -275,8 +336,8 @@ func (pa *AuthorityImpl) WillingToIssue(id core.AcmeIdentifier) error {
// * That the wildcard character is the leftmost label
// * That the wildcard label is not immediately adjacent to a top level ICANN
// TLD
// * That the wildcard wouldn't cover an exact blacklist entry (e.g. an exact
// blacklist entry for "foo.example.com" should prevent issuance for
// * That the wildcard wouldn't cover an exact blocklist entry (e.g. an exact
// blocklist entry for "foo.example.com" should prevent issuance for
// "*.example.com")
//
// If all of the above is true then the base domain (e.g. without the *.) is run
@ -314,13 +375,13 @@ func (pa *AuthorityImpl) WillingToIssueWildcard(ident core.AcmeIdentifier) error
if baseDomain == icannTLD {
return errICANNTLDWildcard
}
// The base domain can't be in the wildcard exact blacklist
// The base domain can't be in the wildcard exact blocklist
if err := pa.checkWildcardHostList(baseDomain); err != nil {
return err
}
// Check that the PA is willing to issue for the base domain
// Since the base domain without the "*." may trip the exact hostname policy
// blacklist when the "*." is removed we replace it with a single "x"
// blocklist when the "*." is removed we replace it with a single "x"
// character to differentiate "*.example.com" from "example.com" for the
// exact hostname check.
//
@ -336,42 +397,42 @@ func (pa *AuthorityImpl) WillingToIssueWildcard(ident core.AcmeIdentifier) error
return pa.WillingToIssue(ident)
}
// checkWildcardHostList checks the wildcardExactBlacklist for a given domain.
// checkWildcardHostList checks the wildcardExactBlocklist for a given domain.
// If the domain is not present on the list nil is returned, otherwise
// errBlacklisted is returned.
// errPolicyForbidden is returned.
func (pa *AuthorityImpl) checkWildcardHostList(domain string) error {
pa.blacklistMu.RLock()
defer pa.blacklistMu.RUnlock()
pa.blocklistMu.RLock()
defer pa.blocklistMu.RUnlock()
if pa.blacklist == nil {
if pa.blocklist == nil {
return fmt.Errorf("Hostname policy not yet loaded.")
}
if pa.wildcardExactBlacklist[domain] {
return errBlacklisted
if pa.wildcardExactBlocklist[domain] {
return errPolicyForbidden
}
return nil
}
func (pa *AuthorityImpl) checkHostLists(domain string) error {
pa.blacklistMu.RLock()
defer pa.blacklistMu.RUnlock()
pa.blocklistMu.RLock()
defer pa.blocklistMu.RUnlock()
if pa.blacklist == nil {
if pa.blocklist == nil {
return fmt.Errorf("Hostname policy not yet loaded.")
}
labels := strings.Split(domain, ".")
for i := range labels {
joined := strings.Join(labels[i:], ".")
if pa.blacklist[joined] {
return errBlacklisted
if pa.blocklist[joined] {
return errPolicyForbidden
}
}
if pa.exactBlacklist[domain] {
return errBlacklisted
if pa.exactBlocklist[domain] {
return errPolicyForbidden
}
return nil
}
@ -430,7 +491,7 @@ func (pa *AuthorityImpl) ChallengesFor(identifier core.AcmeIdentifier) ([]core.C
// ChallengeTypeEnabled returns whether the specified challenge type is enabled
func (pa *AuthorityImpl) ChallengeTypeEnabled(t string) bool {
pa.blacklistMu.RLock()
defer pa.blacklistMu.RUnlock()
pa.blocklistMu.RLock()
defer pa.blocklistMu.RUnlock()
return pa.enabledChallenges[t]
}

View File

@ -2,6 +2,7 @@ package policy
import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
"testing"
@ -10,6 +11,7 @@ import (
"github.com/letsencrypt/boulder/features"
blog "github.com/letsencrypt/boulder/log"
"github.com/letsencrypt/boulder/test"
"gopkg.in/yaml.v2"
)
var log = blog.UseMock()
@ -106,24 +108,29 @@ func TestWillingToIssue(t *testing.T) {
`foo.bd`,
}
shouldBeBlacklisted := []string{
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`,
}
blacklistContents := []string{
blocklistContents := []string{
`website2.com`,
`website2.org`,
`website2.co.uk`,
`website3.com`,
`website4.com`,
}
exactBlacklistContents := []string{
exactBlocklistContents := []string{
`www.website1.org`,
`highvalue.website1.org`,
`dl.website1.org`,
}
adminBlockedContents := []string{
`banned.in.dc.com`,
}
shouldBeAccepted := []string{
`lowvalue.website1.org`,
@ -136,71 +143,93 @@ func TestWillingToIssue(t *testing.T) {
"www.web-site2.com",
}
pa := paImpl(t)
blacklistBytes, err := json.Marshal(blacklistJSON{
Blacklist: blacklistContents,
ExactBlacklist: exactBlacklistContents,
})
test.AssertNotError(t, err, "Couldn't serialize blacklist")
f, _ := ioutil.TempFile("", "test-blacklist.txt")
defer os.Remove(f.Name())
err = ioutil.WriteFile(f.Name(), blacklistBytes, 0640)
test.AssertNotError(t, err, "Couldn't write blacklist")
err = pa.SetHostnamePolicyFile(f.Name())
test.AssertNotError(t, err, "Couldn't load rules")
// Test for invalid identifier type
identifier := core.AcmeIdentifier{Type: "ip", Value: "example.com"}
err = pa.WillingToIssue(identifier)
if err != errInvalidIdentifier {
t.Error("Identifier was not correctly forbidden: ", identifier)
policy := blockedNamesPolicy{
HighRiskBlockedNames: blocklistContents,
ExactBlockedNames: exactBlocklistContents,
AdminBlockedNames: adminBlockedContents,
}
// Test syntax errors
for _, tc := range testCases {
identifier := core.AcmeIdentifier{Type: core.IdentifierDNS, Value: tc.domain}
err := pa.WillingToIssue(identifier)
if err != tc.err {
t.Errorf("WillingToIssue(%q) = %q, expected %q", tc.domain, err, tc.err)
jsonPolicyBytes, err := json.Marshal(policy)
test.AssertNotError(t, err, "Couldn't JSON serialize blocklist")
jsonPolicyFile, _ := ioutil.TempFile("", "test-blocklist.*.json")
defer os.Remove(jsonPolicyFile.Name())
err = ioutil.WriteFile(jsonPolicyFile.Name(), jsonPolicyBytes, 0640)
test.AssertNotError(t, err, "Couldn't write JSON blocklist")
yamlPolicyBytes, err := yaml.Marshal(policy)
test.AssertNotError(t, err, "Couldn't YAML serialize blocklist")
yamlPolicyFile, _ := ioutil.TempFile("", "test-blocklist.*.yaml")
defer os.Remove(yamlPolicyFile.Name())
err = ioutil.WriteFile(yamlPolicyFile.Name(), yamlPolicyBytes, 0640)
test.AssertNotError(t, err, "Couldn't write YAML blocklist")
testPolicyFile := func(f string) {
pa := paImpl(t)
err = pa.SetHostnamePolicyFile(f)
test.AssertNotError(t, err, "Couldn't load rules")
// Test for invalid identifier type
identifier := core.AcmeIdentifier{Type: "ip", Value: "example.com"}
err = pa.WillingToIssue(identifier)
if err != errInvalidIdentifier {
t.Error("Identifier was not correctly forbidden: ", identifier)
}
// Test syntax errors
for _, tc := range testCases {
identifier := core.AcmeIdentifier{Type: core.IdentifierDNS, Value: tc.domain}
err := pa.WillingToIssue(identifier)
if err != tc.err {
t.Errorf("WillingToIssue(%q) = %q, expected %q", tc.domain, err, tc.err)
}
}
// Invalid encoding
err = pa.WillingToIssue(core.AcmeIdentifier{Type: core.IdentifierDNS, Value: "www.xn--m.com"})
test.AssertError(t, err, "WillingToIssue didn't fail on a malformed IDN")
// Valid encoding
err = pa.WillingToIssue(core.AcmeIdentifier{Type: core.IdentifierDNS, Value: "www.xn--mnich-kva.com"})
test.AssertNotError(t, err, "WillingToIssue failed on a properly formed IDN")
// IDN TLD
err = pa.WillingToIssue(core.AcmeIdentifier{Type: core.IdentifierDNS, Value: "xn--example--3bhk5a.xn--p1ai"})
test.AssertNotError(t, err, "WillingToIssue failed on a properly formed domain with IDN TLD")
features.Reset()
// Test domains that are equal to public suffixes
for _, domain := range shouldBeTLDError {
identifier := core.AcmeIdentifier{Type: core.IdentifierDNS, Value: domain}
err := pa.WillingToIssue(identifier)
if err != errICANNTLD {
t.Error("Identifier was not correctly forbidden: ", identifier, err)
}
}
// Test expected blocked domains
for _, domain := range shouldBeBlocked {
identifier := core.AcmeIdentifier{Type: core.IdentifierDNS, Value: domain}
err := pa.WillingToIssue(identifier)
if err != errPolicyForbidden {
t.Error("Identifier was not correctly forbidden: ", identifier, err)
}
}
// Test acceptance of good names
for _, domain := range shouldBeAccepted {
identifier := core.AcmeIdentifier{Type: core.IdentifierDNS, Value: domain}
if err := pa.WillingToIssue(identifier); err != nil {
t.Error("Identifier was incorrectly forbidden: ", identifier, err)
}
}
}
// Invalid encoding
err = pa.WillingToIssue(core.AcmeIdentifier{Type: core.IdentifierDNS, Value: "www.xn--m.com"})
test.AssertError(t, err, "WillingToIssue didn't fail on a malformed IDN")
// Valid encoding
err = pa.WillingToIssue(core.AcmeIdentifier{Type: core.IdentifierDNS, Value: "www.xn--mnich-kva.com"})
test.AssertNotError(t, err, "WillingToIssue failed on a properly formed IDN")
// IDN TLD
err = pa.WillingToIssue(core.AcmeIdentifier{Type: core.IdentifierDNS, Value: "xn--example--3bhk5a.xn--p1ai"})
test.AssertNotError(t, err, "WillingToIssue failed on a properly formed domain with IDN TLD")
features.Reset()
// Test domains that are equal to public suffixes
for _, domain := range shouldBeTLDError {
identifier := core.AcmeIdentifier{Type: core.IdentifierDNS, Value: domain}
err := pa.WillingToIssue(identifier)
if err != errICANNTLD {
t.Error("Identifier was not correctly forbidden: ", identifier, err)
}
}
// Test blacklisting
for _, domain := range shouldBeBlacklisted {
identifier := core.AcmeIdentifier{Type: core.IdentifierDNS, Value: domain}
err := pa.WillingToIssue(identifier)
if err != errBlacklisted {
t.Error("Identifier was not correctly forbidden: ", identifier, err)
}
}
// Test acceptance of good names
for _, domain := range shouldBeAccepted {
identifier := core.AcmeIdentifier{Type: core.IdentifierDNS, Value: domain}
if err := pa.WillingToIssue(identifier); err != nil {
t.Error("Identifier was incorrectly forbidden: ", identifier, err)
}
// Both the JSON and the YAML policy files should behave the exact same way
// when tested.
for _, f := range []string{
jsonPolicyFile.Name(),
yamlPolicyFile.Name(),
} {
testPolicyFile(f)
}
}
@ -213,12 +242,12 @@ func TestWillingToIssueWildcard(t *testing.T) {
}
pa := paImpl(t)
bannedBytes, err := json.Marshal(blacklistJSON{
Blacklist: bannedDomains,
ExactBlacklist: exactBannedDomains,
bannedBytes, err := json.Marshal(blockedNamesPolicy{
HighRiskBlockedNames: bannedDomains,
ExactBlockedNames: exactBannedDomains,
})
test.AssertNotError(t, err, "Couldn't serialize banned list")
f, _ := ioutil.TempFile("", "test-wildcard-banlist.txt")
f, _ := ioutil.TempFile("", "test-wildcard-banlist.*.json")
defer os.Remove(f.Name())
err = ioutil.WriteFile(f.Name(), bannedBytes, 0640)
test.AssertNotError(t, err, "Couldn't write serialized banned list to file")
@ -265,26 +294,26 @@ func TestWillingToIssueWildcard(t *testing.T) {
{
Name: "Forbidden base domain",
Ident: makeDNSIdent("*.zombo.gov.us"),
ExpectedErr: errBlacklisted,
ExpectedErr: errPolicyForbidden,
},
// We should not allow getting a wildcard for that would cover an exact
// blocklist domain
{
Name: "Wildcard for ExactBlacklist base domain",
Name: "Wildcard for ExactBlocklist base domain",
Ident: makeDNSIdent("*.letsdecrypt.org"),
ExpectedErr: errBlacklisted,
ExpectedErr: errPolicyForbidden,
},
// We should allow a wildcard for a domain that doesn't match the exact
// blacklist domain
// blocklist domain
{
Name: "Wildcard for non-matching subdomain of ExactBlacklist domain",
Name: "Wildcard for non-matching subdomain of ExactBlocklist domain",
Ident: makeDNSIdent("*.lowvalue.letsdecrypt.org"),
ExpectedErr: nil,
},
// We should allow getting a wildcard for an exact blacklist domain since it
// We should allow getting a wildcard for an exact blocklist domain since it
// only covers subdomains, not the exact name.
{
Name: "Wildcard for ExactBlacklist domain",
Name: "Wildcard for ExactBlocklist domain",
Ident: makeDNSIdent("*.highvalue.letsdecrypt.org"),
ExpectedErr: nil,
},
@ -365,9 +394,9 @@ func TestChallengesForWildcard(t *testing.T) {
test.AssertEquals(t, challenges[0].Type, core.ChallengeTypeDNS01)
}
// TestMalformedExactBlacklist tests that loading a JSON policy file with an
// invalid exact blacklist entry will fail as expected.
func TestMalformedExactBlacklist(t *testing.T) {
// TestMalformedExactBlocklist tests that loading a JSON policy file with an
// invalid exact blocklist entry will fail as expected.
func TestMalformedExactBlocklist(t *testing.T) {
pa := paImpl(t)
exactBannedDomains := []string{
@ -379,22 +408,36 @@ func TestMalformedExactBlacklist(t *testing.T) {
}
// Create JSON for the exactBannedDomains
bannedBytes, err := json.Marshal(blacklistJSON{
Blacklist: bannedDomains,
ExactBlacklist: exactBannedDomains,
bannedBytes, err := json.Marshal(blockedNamesPolicy{
HighRiskBlockedNames: bannedDomains,
ExactBlockedNames: exactBannedDomains,
})
test.AssertNotError(t, err, "Couldn't serialize banned list")
// Create a temp file for the JSON contents
f, _ := ioutil.TempFile("", "test-invalid-exactblacklist.json")
f, _ := ioutil.TempFile("", "test-invalid-exactblocklist.*.json")
defer os.Remove(f.Name())
// Write the JSON to the temp file
err = ioutil.WriteFile(f.Name(), bannedBytes, 0640)
test.AssertNotError(t, err, "Couldn't write serialized banned list to file")
// Try to use the JSON tempfile as the hostname policy. It should produce an
// error since the exact blacklist contents are malformed.
// error since the exact blocklist contents are malformed.
err = pa.SetHostnamePolicyFile(f.Name())
test.AssertError(t, err, "Loaded invalid exact blacklist content without error")
test.AssertEquals(t, err.Error(), "Malformed exact blacklist entry, only one label: \"com\"")
test.AssertError(t, err, "Loaded invalid exact blocklist content without error")
test.AssertEquals(t, err.Error(), "Malformed ExactBlockedNames entry, only one label: \"com\"")
}
func TestSetHostnamePolicyFileExtension(t *testing.T) {
filename := "hostname.policy.json.j2"
expectedErrMsg := fmt.Sprintf(
`Hostname policy file %q has unknown extension. Supported: .yml,.yaml,.json`,
filename)
pa := paImpl(t)
if err := pa.SetHostnamePolicyFile(filename); err != nil && err.Error() != expectedErrMsg {
t.Errorf("expected SetHostnamePolicyFile error %q got %q", expectedErrMsg, err.Error())
} else if err == nil {
t.Errorf("expected SetHostnamePolicyFile error %q got nil", expectedErrMsg)
}
}

View File

@ -41,7 +41,7 @@
"lifespanOCSP": "96h",
"maxNames": 100,
"enableMustStaple": true,
"hostnamePolicyFile": "test/hostname-policy.json",
"hostnamePolicyFile": "test/hostname-policy.yaml",
"cfssl": {
"signing": {
"profiles": {

View File

@ -41,7 +41,7 @@
"lifespanOCSP": "96h",
"maxNames": 100,
"enableMustStaple": true,
"hostnamePolicyFile": "test/hostname-policy.json",
"hostnamePolicyFile": "test/hostname-policy.yaml",
"cfssl": {
"signing": {
"profiles": {

View File

@ -2,7 +2,7 @@
"certChecker": {
"dbConnectFile": "test/secrets/cert_checker_dburl",
"maxDBConns": 10,
"hostnamePolicyFile": "test/hostname-policy.json"
"hostnamePolicyFile": "test/hostname-policy.yaml"
},
"pa": {

View File

@ -4,7 +4,7 @@
"maxConcurrentRPCServerRequests": 100000,
"maxContactsPerRegistration": 100,
"debugAddr": ":8002",
"hostnamePolicyFile": "test/hostname-policy.json",
"hostnamePolicyFile": "test/hostname-policy.yaml",
"maxNames": 100,
"reuseValidAuthz": true,
"authorizationLifetimeDays": 30,

33
test/hostname-policy.yaml Normal file
View File

@ -0,0 +1,33 @@
#
# Example YAML Boulder hostname policy
#
# This is *not* a production ready policy file and not reflective of Let's
# Encrypt's policies! It is just an example.
# ExactBlockedNames prevent issuance for the exact names listed, as well as
# their wildcard form.
ExactBlockedNames:
- "highrisk.le-test.hoffman-andrews.com"
- "exactblacklist.letsencrypt.org"
# HighRiskBlockedNames prevent issuance for the exact names listed as well as
# all subdomains/wildcards.
HighRiskBlockedNames:
# See RFC 3152
- "ipv6.arpa"
# See RFC 2317
- "in-addr.arpa"
# Etc etc etc
- "example"
- "example.net"
- "example.org"
- "invalid"
- "local"
- "localhost"
- "test"
# AdminBlockedNames are treated the same as HighRiskBlockedNames by Boulder but
# since they change more frequently based on administrative action over time
# they are separated into their own list.
AdminBlockedNames:
- "sealand"