RA and WFE tests: use inmem rate limit source (#7859)

The purpose of these RA and WFE unit tests is to test how they deal with
certain rate limit conditions, not to test talking to an actual redis
instance. Streamline the tests by having them talk to an in-memory rate
limits store, rather than a redis-backed one.
This commit is contained in:
Aaron Gable 2024-12-03 14:52:16 -08:00 committed by GitHub
parent bac5602c6d
commit d962c61067
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 17 additions and 73 deletions

View File

@ -16,7 +16,6 @@ import (
"fmt"
"math/big"
mrand "math/rand/v2"
"os"
"regexp"
"strconv"
"strings"
@ -40,7 +39,6 @@ import (
akamaipb "github.com/letsencrypt/boulder/akamai/proto"
capb "github.com/letsencrypt/boulder/ca/proto"
"github.com/letsencrypt/boulder/cmd"
"github.com/letsencrypt/boulder/config"
"github.com/letsencrypt/boulder/core"
corepb "github.com/letsencrypt/boulder/core/proto"
@ -60,7 +58,6 @@ import (
rapb "github.com/letsencrypt/boulder/ra/proto"
"github.com/letsencrypt/boulder/ratelimit"
"github.com/letsencrypt/boulder/ratelimits"
bredis "github.com/letsencrypt/boulder/redis"
"github.com/letsencrypt/boulder/sa"
sapb "github.com/letsencrypt/boulder/sa/proto"
"github.com/letsencrypt/boulder/test"
@ -283,7 +280,7 @@ func newAcctKey(t *testing.T) []byte {
return acctKey
}
func initAuthorities(t *testing.T) (*DummyValidationAuthority, sapb.StorageAuthorityClient, *RegistrationAuthorityImpl, *ratelimits.RedisSource, clock.FakeClock, func()) {
func initAuthorities(t *testing.T) (*DummyValidationAuthority, sapb.StorageAuthorityClient, *RegistrationAuthorityImpl, ratelimits.Source, clock.FakeClock, func()) {
err := json.Unmarshal(AccountKeyJSONA, &AccountKeyA)
test.AssertNotError(t, err, "Failed to unmarshal public JWK")
err = json.Unmarshal(AccountKeyJSONB, &AccountKeyB)
@ -352,39 +349,11 @@ func initAuthorities(t *testing.T) (*DummyValidationAuthority, sapb.StorageAutho
},
}, nil, nil, 0, log, metrics.NoopRegisterer)
var source *ratelimits.RedisSource
var limiter *ratelimits.Limiter
var txnBuilder *ratelimits.TransactionBuilder
if strings.Contains(os.Getenv("BOULDER_CONFIG_DIR"), "test/config-next") {
rc := bredis.Config{
Username: "unittest-rw",
TLS: cmd.TLSConfig{
CACertFile: "../test/certs/ipki/minica.pem",
CertFile: "../test/certs/ipki/localhost/cert.pem",
KeyFile: "../test/certs/ipki/localhost/key.pem",
},
Lookups: []cmd.ServiceDomain{
{
Service: "redisratelimits",
Domain: "service.consul",
},
},
LookupDNSAuthority: "consul.service.consul",
}
rc.PasswordConfig = cmd.PasswordConfig{
PasswordFile: "../test/secrets/ratelimits_redis_password",
}
ring, err := bredis.NewRingFromConfig(rc, stats, log)
test.AssertNotError(t, err, "making redis ring client")
source = ratelimits.NewRedisSource(ring.Ring, fc, stats)
test.AssertNotNil(t, source, "source should not be nil")
err = source.Ping(context.Background())
test.AssertNotError(t, err, "Ping should not error")
limiter, err = ratelimits.NewLimiter(fc, source, stats)
test.AssertNotError(t, err, "making limiter")
txnBuilder, err = ratelimits.NewTransactionBuilder("../test/config-next/wfe2-ratelimit-defaults.yml", "")
test.AssertNotError(t, err, "making transaction composer")
}
rlSource := ratelimits.NewInmemSource()
limiter, err := ratelimits.NewLimiter(fc, rlSource, stats)
test.AssertNotError(t, err, "making limiter")
txnBuilder, err := ratelimits.NewTransactionBuilder("../test/config-next/wfe2-ratelimit-defaults.yml", "")
test.AssertNotError(t, err, "making transaction composer")
testKeyPolicy, err := goodkey.NewPolicy(nil, nil)
test.AssertNotError(t, err, "making keypolicy")
@ -401,7 +370,7 @@ func initAuthorities(t *testing.T) (*DummyValidationAuthority, sapb.StorageAutho
ra.CA = ca
ra.OCSP = &mocks.MockOCSPGenerator{}
ra.PA = pa
return va, sa, ra, source, fc, cleanUp
return va, sa, ra, rlSource, fc, cleanUp
}
func TestValidateContacts(t *testing.T) {

View File

@ -34,7 +34,7 @@ var allowedDecision = &Decision{allowed: true, remaining: math.MaxInt64}
// utilizing a leaky bucket-style approach.
type Limiter struct {
// source is used to store buckets. It must be safe for concurrent use.
source source
source Source
clk clock.Clock
spendLatency *prometheus.HistogramVec
@ -43,7 +43,7 @@ type Limiter struct {
// NewLimiter returns a new *Limiter. The provided source must be safe for
// concurrent use.
func NewLimiter(clk clock.Clock, source source, stats prometheus.Registerer) (*Limiter, error) {
func NewLimiter(clk clock.Clock, source Source, stats prometheus.Registerer) (*Limiter, error) {
spendLatency := prometheus.NewHistogramVec(prometheus.HistogramOpts{
Name: "ratelimits_spend_latency",
Help: fmt.Sprintf("Latency of ratelimit checks labeled by limit=[name] and decision=[%s|%s], in seconds", Allowed, Denied),

View File

@ -21,7 +21,7 @@ import (
const tenZeroZeroTwo = "10.0.0.2"
// newTestLimiter constructs a new limiter.
func newTestLimiter(t *testing.T, s source, clk clock.FakeClock) *Limiter {
func newTestLimiter(t *testing.T, s Source, clk clock.FakeClock) *Limiter {
l, err := NewLimiter(clk, s, metrics.NoopRegisterer)
test.AssertNotError(t, err, "should not error")
return l

View File

@ -10,8 +10,8 @@ import (
// ErrBucketNotFound indicates that the bucket was not found.
var ErrBucketNotFound = fmt.Errorf("bucket not found")
// source is an interface for creating and modifying TATs.
type source interface {
// Source is an interface for creating and modifying TATs.
type Source interface {
// BatchSet stores the TATs at the specified bucketKeys (formatted as
// 'name:id'). Implementations MUST ensure non-blocking operations by
// either:
@ -64,9 +64,9 @@ type inmem struct {
m map[string]time.Time
}
var _ source = (*inmem)(nil)
var _ Source = (*inmem)(nil)
func newInmem() *inmem {
func NewInmemSource() *inmem {
return &inmem{m: make(map[string]time.Time)}
}

View File

@ -12,7 +12,7 @@ import (
)
// Compile-time check that RedisSource implements the source interface.
var _ source = (*RedisSource)(nil)
var _ Source = (*RedisSource)(nil)
// RedisSource is a ratelimits source backed by sharded Redis.
type RedisSource struct {

View File

@ -7,5 +7,5 @@ import (
)
func newInmemTestLimiter(t *testing.T, clk clock.FakeClock) *Limiter {
return newTestLimiter(t, newInmem(), clk)
return newTestLimiter(t, NewInmemSource(), clk)
}

View File

@ -52,7 +52,6 @@ import (
"github.com/letsencrypt/boulder/probs"
rapb "github.com/letsencrypt/boulder/ra/proto"
"github.com/letsencrypt/boulder/ratelimits"
bredis "github.com/letsencrypt/boulder/redis"
"github.com/letsencrypt/boulder/revocation"
sapb "github.com/letsencrypt/boulder/sa/proto"
"github.com/letsencrypt/boulder/test"
@ -394,8 +393,6 @@ func setupWFE(t *testing.T) (WebFrontEndImpl, clock.FakeClock, requestSigner) {
mockSA := mocks.NewStorageAuthorityReadOnly(fc)
log := blog.NewMock()
// Use derived nonces.
rncKey := []byte("b8c758dd85e113ea340ce0b3a99f389d40a308548af94d1730a7692c1874f1f")
noncePrefix := nonce.DerivePrefix("192.168.1.1:8080", rncKey)
@ -407,29 +404,7 @@ func setupWFE(t *testing.T) (WebFrontEndImpl, clock.FakeClock, requestSigner) {
rnc := inmemNonceService
// Setup rate limiting.
rc := bredis.Config{
Username: "unittest-rw",
TLS: cmd.TLSConfig{
CACertFile: "../test/certs/ipki/minica.pem",
CertFile: "../test/certs/ipki/localhost/cert.pem",
KeyFile: "../test/certs/ipki/localhost/key.pem",
},
Lookups: []cmd.ServiceDomain{
{
Service: "redisratelimits",
Domain: "service.consul",
},
},
LookupDNSAuthority: "consul.service.consul",
}
rc.PasswordConfig = cmd.PasswordConfig{
PasswordFile: "../test/secrets/ratelimits_redis_password",
}
ring, err := bredis.NewRingFromConfig(rc, stats, log)
test.AssertNotError(t, err, "making redis ring client")
source := ratelimits.NewRedisSource(ring.Ring, fc, stats)
test.AssertNotNil(t, source, "source should not be nil")
limiter, err := ratelimits.NewLimiter(fc, source, stats)
limiter, err := ratelimits.NewLimiter(fc, ratelimits.NewInmemSource(), stats)
test.AssertNotError(t, err, "making limiter")
txnBuilder, err := ratelimits.NewTransactionBuilder("../test/config-next/wfe2-ratelimit-defaults.yml", "")
test.AssertNotError(t, err, "making transaction composer")