...

Package ratelimits

import "github.com/letsencrypt/boulder/ratelimits"
Overview
Index

Overview ▾

Constants

const (
    // Allowed is used for rate limit metrics, it's the value of the 'decision'
    // label when a request was allowed.
    Allowed = "allowed"

    // Denied is used for rate limit metrics, it's the value of the 'decision'
    // label when a request was denied.
    Denied = "denied"
)

Variables

ErrBucketNotFound indicates that the bucket was not found.

var ErrBucketNotFound = fmt.Errorf("bucket not found")

ErrInvalidCost indicates that the cost specified was <= 0.

var ErrInvalidCost = fmt.Errorf("invalid cost, must be > 0")

ErrInvalidCostForCheck indicates that the check cost specified was < 0.

var ErrInvalidCostForCheck = fmt.Errorf("invalid check cost, must be >= 0")

ErrInvalidCostOverLimit indicates that the cost specified was > limit.Burst.

var ErrInvalidCostOverLimit = fmt.Errorf("invalid cost, must be <= limit.Burst")

type Decision

type Decision struct {
    // Allowed is true if the bucket possessed enough capacity to allow the
    // request given the cost.
    Allowed bool

    // Remaining is the number of requests the client is allowed to make before
    // they're rate limited.
    Remaining int64

    // RetryIn is the duration the client MUST wait before they're allowed to
    // make a request.
    RetryIn time.Duration

    // ResetIn is the duration the bucket will take to refill to its maximum
    // capacity, assuming no further requests are made.
    ResetIn time.Duration
    // contains filtered or unexported fields
}

type Limiter

Limiter provides a high-level interface for rate limiting requests by utilizing a leaky bucket-style approach.

type Limiter struct {
    // contains filtered or unexported fields
}

func NewLimiter

func NewLimiter(clk clock.Clock, source source, defaults, overrides string, stats prometheus.Registerer) (*Limiter, error)

NewLimiter returns a new *Limiter. The provided source must be safe for concurrent use. The defaults and overrides paths are expected to be paths to YAML files that contain the default and override limits, respectively. The overrides file is optional, all other arguments are required.

func (*Limiter) Check

func (l *Limiter) Check(ctx context.Context, name Name, id string, cost int64) (*Decision, error)

Check returns a *Decision that indicates whether there's enough capacity to allow the request, given the cost, for the specified limit Name and client id. However, it DOES NOT deduct the cost of the request from the bucket's capacity. Hence, the returned *Decision represents the hypothetical state of the bucket if the cost WERE to be deducted. The returned *Decision will always include the number of remaining requests in the bucket, the required wait time before the client can make another request, and the time until the bucket refills to its maximum capacity (resets). If no bucket exists for the given limit Name and client id, a new one will be created WITHOUT the request's cost deducted from its initial capacity. If the specified limit is disabled, ErrLimitDisabled is returned.

func (*Limiter) Refund

func (l *Limiter) Refund(ctx context.Context, name Name, id string, cost int64) (*Decision, error)

Refund attempts to refund the cost to the bucket identified by limit name and client id. The returned *Decision indicates whether the refund was successful or not. If the refund was successful, the cost of the request was added back to the bucket's capacity. If the refund is not possible (i.e., the bucket is already full or the refund amount is invalid), no cost is refunded.

Note: The amount refunded cannot cause the bucket to exceed its maximum capacity. However, partial refunds are allowed and are considered successful. For instance, if a bucket has a maximum capacity of 10 and currently has 5 requests remaining, a refund request of 7 will result in the bucket reaching its maximum capacity of 10, not 12.

func (*Limiter) Reset

func (l *Limiter) Reset(ctx context.Context, name Name, id string) error

Reset resets the specified bucket.

func (*Limiter) Spend

func (l *Limiter) Spend(ctx context.Context, name Name, id string, cost int64) (*Decision, error)

Spend returns a *Decision that indicates if enough capacity was available to process the request, given the cost, for the specified limit Name and client id. If capacity existed, the cost of the request HAS been deducted from the bucket's capacity, otherwise no cost was deducted. The returned *Decision will always include the number of remaining requests in the bucket, the required wait time before the client can make another request, and the time until the bucket refills to its maximum capacity (resets). If no bucket exists for the given limit Name and client id, a new one will be created WITH the request's cost deducted from its initial capacity. If the specified limit is disabled, ErrLimitDisabled is returned.

type Name

Name is an enumeration of all rate limit names. It is used to intern rate limit names as strings and to provide a type-safe way to refer to rate limits.

IMPORTANT: If you add a new limit Name, you MUST add it to the 'nameToString' mapping and idValidForName function below.

type Name int
const (
    // Unknown is the zero value of Name and is used to indicate an unknown
    // limit name.
    Unknown Name = iota

    // NewRegistrationsPerIPAddress uses bucket key 'enum:ipAddress'.
    NewRegistrationsPerIPAddress

    // NewRegistrationsPerIPv6Range uses bucket key 'enum:ipv6rangeCIDR'. The
    // address range must be a /48. RFC 3177, which was published in 2001,
    // advised operators to allocate a /48 block of IPv6 addresses for most end
    // sites. RFC 6177, which was published in 2011 and obsoletes RFC 3177,
    // advises allocating a smaller /56 block. We've chosen to use the larger
    // /48 block for our IPv6 rate limiting. See:
    //   1. https://tools.ietf.org/html/rfc3177#section-3
    //   2. https://datatracker.ietf.org/doc/html/rfc6177#section-2
    NewRegistrationsPerIPv6Range

    // NewOrdersPerAccount uses bucket key 'enum:regId'.
    NewOrdersPerAccount

    // FailedAuthorizationsPerAccount uses bucket key 'enum:regId', where regId
    // is the registration id of the account.
    FailedAuthorizationsPerAccount

    // CertificatesPerDomainPerAccount uses bucket key 'enum:regId:domain',
    // where name is the a name in a certificate issued to the account matching
    // regId.
    CertificatesPerDomainPerAccount

    // CertificatesPerFQDNSetPerAccount uses bucket key 'enum:regId:fqdnSet',
    // where nameSet is a set of names in a certificate issued to the account
    // matching regId.
    CertificatesPerFQDNSetPerAccount
)

func (Name) String

func (n Name) String() string

String returns the string representation of the Name. It allows Name to satisfy the fmt.Stringer interface.

type RedisSource

RedisSource is a ratelimits source backed by sharded Redis.

type RedisSource struct {
    // contains filtered or unexported fields
}

func NewRedisSource

func NewRedisSource(client *redis.Ring, clk clock.Clock, stats prometheus.Registerer) *RedisSource

NewRedisSource returns a new Redis backed source using the provided *redis.Ring client.

func (*RedisSource) Delete

func (r *RedisSource) Delete(ctx context.Context, bucketKey string) error

Delete deletes the TAT at the specified bucketKey ('name:id'). It returns an error if the operation failed and nil otherwise. A nil return value does not indicate that the bucketKey existed.

func (*RedisSource) Get

func (r *RedisSource) Get(ctx context.Context, bucketKey string) (time.Time, error)

Get retrieves the TAT at the specified bucketKey ('name:id'). It returns the TAT and nil if the operation succeeded, or an error if the operation failed. If the bucketKey does not exist, it returns ErrBucketNotFound.

func (*RedisSource) Ping

func (r *RedisSource) Ping(ctx context.Context) error

Ping checks that each shard of the *redis.Ring is reachable using the PING command. It returns an error if any shard is unreachable and nil otherwise.

func (*RedisSource) Set

func (r *RedisSource) Set(ctx context.Context, bucketKey string, tat time.Time) error

Set stores the TAT at the specified bucketKey ('name:id'). It returns an error if the operation failed and nil otherwise. If the bucketKey does not exist, it will be created.