Merge pull request #958 from bifurcation/limit-contacts
Limit the number of contacts per registration
This commit is contained in:
commit
0e034fcda4
|
@ -46,7 +46,8 @@ func main() {
|
||||||
rateLimitPolicies, err := cmd.LoadRateLimitPolicies(c.RA.RateLimitPoliciesFilename)
|
rateLimitPolicies, err := cmd.LoadRateLimitPolicies(c.RA.RateLimitPoliciesFilename)
|
||||||
cmd.FailOnError(err, "Couldn't load rate limit policies file")
|
cmd.FailOnError(err, "Couldn't load rate limit policies file")
|
||||||
|
|
||||||
rai := ra.NewRegistrationAuthorityImpl(clock.Default(), auditlogger, stats, rateLimitPolicies)
|
rai := ra.NewRegistrationAuthorityImpl(clock.Default(), auditlogger, stats,
|
||||||
|
rateLimitPolicies, c.RA.MaxContactsPerRegistration)
|
||||||
rai.PA = pa
|
rai.PA = pa
|
||||||
raDNSTimeout, err := time.ParseDuration(c.Common.DNSTimeout)
|
raDNSTimeout, err := time.ParseDuration(c.Common.DNSTimeout)
|
||||||
cmd.FailOnError(err, "Couldn't parse RA DNS timeout")
|
cmd.FailOnError(err, "Couldn't parse RA DNS timeout")
|
||||||
|
|
|
@ -99,6 +99,8 @@ type Config struct {
|
||||||
|
|
||||||
MaxConcurrentRPCServerRequests int64
|
MaxConcurrentRPCServerRequests int64
|
||||||
|
|
||||||
|
MaxContactsPerRegistration int
|
||||||
|
|
||||||
// DebugAddr is the address to run the /debug handlers on.
|
// DebugAddr is the address to run the /debug handlers on.
|
||||||
DebugAddr string
|
DebugAddr string
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,10 +51,11 @@ type RegistrationAuthorityImpl struct {
|
||||||
tiMu *sync.RWMutex
|
tiMu *sync.RWMutex
|
||||||
totalIssuedCache int
|
totalIssuedCache int
|
||||||
lastIssuedCount *time.Time
|
lastIssuedCount *time.Time
|
||||||
|
maxContactsPerReg int
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewRegistrationAuthorityImpl constructs a new RA object.
|
// NewRegistrationAuthorityImpl constructs a new RA object.
|
||||||
func NewRegistrationAuthorityImpl(clk clock.Clock, logger *blog.AuditLogger, stats statsd.Statter, policies cmd.RateLimitConfig) RegistrationAuthorityImpl {
|
func NewRegistrationAuthorityImpl(clk clock.Clock, logger *blog.AuditLogger, stats statsd.Statter, policies cmd.RateLimitConfig, maxContactsPerReg int) RegistrationAuthorityImpl {
|
||||||
ra := RegistrationAuthorityImpl{
|
ra := RegistrationAuthorityImpl{
|
||||||
stats: stats,
|
stats: stats,
|
||||||
clk: clk,
|
clk: clk,
|
||||||
|
@ -62,6 +63,7 @@ func NewRegistrationAuthorityImpl(clk clock.Clock, logger *blog.AuditLogger, sta
|
||||||
authorizationLifetime: DefaultAuthorizationLifetime,
|
authorizationLifetime: DefaultAuthorizationLifetime,
|
||||||
rlPolicies: policies,
|
rlPolicies: policies,
|
||||||
tiMu: new(sync.RWMutex),
|
tiMu: new(sync.RWMutex),
|
||||||
|
maxContactsPerReg: maxContactsPerReg,
|
||||||
}
|
}
|
||||||
return ra
|
return ra
|
||||||
}
|
}
|
||||||
|
@ -176,7 +178,7 @@ func (ra *RegistrationAuthorityImpl) NewRegistration(init core.Registration) (re
|
||||||
// MergeUpdate. But we need to fill it in for new registrations.
|
// MergeUpdate. But we need to fill it in for new registrations.
|
||||||
reg.InitialIP = init.InitialIP
|
reg.InitialIP = init.InitialIP
|
||||||
|
|
||||||
err = validateContacts(reg.Contact, ra.DNSResolver, ra.stats)
|
err = ra.validateContacts(reg.Contact)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -193,15 +195,20 @@ func (ra *RegistrationAuthorityImpl) NewRegistration(init core.Registration) (re
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateContacts(contacts []*core.AcmeURL, resolver core.DNSResolver, stats statsd.Statter) (err error) {
|
func (ra *RegistrationAuthorityImpl) validateContacts(contacts []*core.AcmeURL) (err error) {
|
||||||
|
if ra.maxContactsPerReg > 0 && len(contacts) > ra.maxContactsPerReg {
|
||||||
|
return core.MalformedRequestError(fmt.Sprintf("Too many contacts provided: %d > %d",
|
||||||
|
len(contacts), ra.maxContactsPerReg))
|
||||||
|
}
|
||||||
|
|
||||||
for _, contact := range contacts {
|
for _, contact := range contacts {
|
||||||
switch contact.Scheme {
|
switch contact.Scheme {
|
||||||
case "tel":
|
case "tel":
|
||||||
continue
|
continue
|
||||||
case "mailto":
|
case "mailto":
|
||||||
rtt, err := validateEmail(contact.Opaque, resolver)
|
rtt, err := validateEmail(contact.Opaque, ra.DNSResolver)
|
||||||
stats.TimingDuration("RA.DNS.RTT.MX", rtt, 1.0)
|
ra.stats.TimingDuration("RA.DNS.RTT.MX", rtt, 1.0)
|
||||||
stats.Inc("RA.DNS.Rate", 1, 1.0)
|
ra.stats.Inc("RA.DNS.Rate", 1, 1.0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -576,7 +583,7 @@ func (ra *RegistrationAuthorityImpl) checkLimits(names []string, regID int64) er
|
||||||
func (ra *RegistrationAuthorityImpl) UpdateRegistration(base core.Registration, update core.Registration) (reg core.Registration, err error) {
|
func (ra *RegistrationAuthorityImpl) UpdateRegistration(base core.Registration, update core.Registration) (reg core.Registration, err error) {
|
||||||
base.MergeUpdate(update)
|
base.MergeUpdate(update)
|
||||||
|
|
||||||
err = validateContacts(base.Contact, ra.DNSResolver, ra.stats)
|
err = ra.validateContacts(base.Contact)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -224,7 +224,7 @@ func initAuthorities(t *testing.T) (*DummyValidationAuthority, *sa.SQLStorageAut
|
||||||
Threshold: 100,
|
Threshold: 100,
|
||||||
Window: cmd.ConfigDuration{Duration: 24 * 90 * time.Hour},
|
Window: cmd.ConfigDuration{Duration: 24 * 90 * time.Hour},
|
||||||
},
|
},
|
||||||
})
|
}, 1)
|
||||||
ra.SA = ssa
|
ra.SA = ssa
|
||||||
ra.VA = va
|
ra.VA = va
|
||||||
ra.CA = &ca
|
ra.CA = &ca
|
||||||
|
@ -259,30 +259,34 @@ func assertAuthzEqual(t *testing.T, a1, a2 core.Authorization) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestValidateContacts(t *testing.T) {
|
func TestValidateContacts(t *testing.T) {
|
||||||
|
_, _, ra, _, cleanUp := initAuthorities(t)
|
||||||
|
defer cleanUp()
|
||||||
|
|
||||||
tel, _ := core.ParseAcmeURL("tel:")
|
tel, _ := core.ParseAcmeURL("tel:")
|
||||||
ansible, _ := core.ParseAcmeURL("ansible:earth.sol.milkyway.laniakea/letsencrypt")
|
ansible, _ := core.ParseAcmeURL("ansible:earth.sol.milkyway.laniakea/letsencrypt")
|
||||||
validEmail, _ := core.ParseAcmeURL("mailto:admin@email.com")
|
validEmail, _ := core.ParseAcmeURL("mailto:admin@email.com")
|
||||||
invalidEmail, _ := core.ParseAcmeURL("mailto:admin@example.com")
|
invalidEmail, _ := core.ParseAcmeURL("mailto:admin@example.com")
|
||||||
malformedEmail, _ := core.ParseAcmeURL("mailto:admin.com")
|
malformedEmail, _ := core.ParseAcmeURL("mailto:admin.com")
|
||||||
|
|
||||||
nStats, _ := statsd.NewNoopClient()
|
err := ra.validateContacts([]*core.AcmeURL{})
|
||||||
|
|
||||||
err := validateContacts([]*core.AcmeURL{}, &mocks.DNSResolver{}, nStats)
|
|
||||||
test.AssertNotError(t, err, "No Contacts")
|
test.AssertNotError(t, err, "No Contacts")
|
||||||
|
|
||||||
err = validateContacts([]*core.AcmeURL{tel}, &mocks.DNSResolver{}, nStats)
|
err = ra.validateContacts([]*core.AcmeURL{tel, validEmail})
|
||||||
|
test.AssertError(t, err, "Too Many Contacts")
|
||||||
|
|
||||||
|
err = ra.validateContacts([]*core.AcmeURL{tel})
|
||||||
test.AssertNotError(t, err, "Simple Telephone")
|
test.AssertNotError(t, err, "Simple Telephone")
|
||||||
|
|
||||||
err = validateContacts([]*core.AcmeURL{validEmail}, &mocks.DNSResolver{}, nStats)
|
err = ra.validateContacts([]*core.AcmeURL{validEmail})
|
||||||
test.AssertNotError(t, err, "Valid Email")
|
test.AssertNotError(t, err, "Valid Email")
|
||||||
|
|
||||||
err = validateContacts([]*core.AcmeURL{invalidEmail}, &mocks.DNSResolver{}, nStats)
|
err = ra.validateContacts([]*core.AcmeURL{invalidEmail})
|
||||||
test.AssertError(t, err, "Invalid Email")
|
test.AssertError(t, err, "Invalid Email")
|
||||||
|
|
||||||
err = validateContacts([]*core.AcmeURL{malformedEmail}, &mocks.DNSResolver{}, nStats)
|
err = ra.validateContacts([]*core.AcmeURL{malformedEmail})
|
||||||
test.AssertError(t, err, "Malformed Email")
|
test.AssertError(t, err, "Malformed Email")
|
||||||
|
|
||||||
err = validateContacts([]*core.AcmeURL{ansible}, &mocks.DNSResolver{}, nStats)
|
err = ra.validateContacts([]*core.AcmeURL{ansible})
|
||||||
test.AssertError(t, err, "Unknown scehme")
|
test.AssertError(t, err, "Unknown scehme")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -121,6 +121,7 @@
|
||||||
"ra": {
|
"ra": {
|
||||||
"rateLimitPoliciesFilename": "test/rate-limit-policies.yml",
|
"rateLimitPoliciesFilename": "test/rate-limit-policies.yml",
|
||||||
"maxConcurrentRPCServerRequests": 16,
|
"maxConcurrentRPCServerRequests": 16,
|
||||||
|
"maxContactsPerRegistration": 100,
|
||||||
"debugAddr": "localhost:8002"
|
"debugAddr": "localhost:8002"
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -547,7 +547,7 @@ func TestIssueCertificate(t *testing.T) {
|
||||||
// TODO: Use a mock RA so we can test various conditions of authorized, not
|
// TODO: Use a mock RA so we can test various conditions of authorized, not
|
||||||
// authorized, etc.
|
// authorized, etc.
|
||||||
stats, _ := statsd.NewNoopClient(nil)
|
stats, _ := statsd.NewNoopClient(nil)
|
||||||
ra := ra.NewRegistrationAuthorityImpl(fakeClock, wfe.log, stats, cmd.RateLimitConfig{})
|
ra := ra.NewRegistrationAuthorityImpl(fakeClock, wfe.log, stats, cmd.RateLimitConfig{}, 0)
|
||||||
ra.SA = &mocks.StorageAuthority{}
|
ra.SA = &mocks.StorageAuthority{}
|
||||||
ra.CA = &MockCA{}
|
ra.CA = &MockCA{}
|
||||||
ra.PA = &MockPA{}
|
ra.PA = &MockPA{}
|
||||||
|
|
Loading…
Reference in New Issue