Documentation ¶
Index ¶
- Constants
- Variables
- func FQDNsToETLDsPlusOne(names []string) []string
- func NewInmemSource() *inmem
- func NewRegIdDomainBucketKey(name Name, regId int64, orderName string) (string, error)
- type Decision
- type LimitConfig
- type LimitConfigs
- type Limiter
- func (l *Limiter) BatchRefund(ctx context.Context, txns []Transaction) (*Decision, error)
- func (l *Limiter) BatchSpend(ctx context.Context, txns []Transaction) (*Decision, error)
- func (l *Limiter) Check(ctx context.Context, txn Transaction) (*Decision, error)
- func (l *Limiter) Refund(ctx context.Context, txn Transaction) (*Decision, error)
- func (l *Limiter) Reset(ctx context.Context, bucketKey string) error
- func (l *Limiter) Spend(ctx context.Context, txn Transaction) (*Decision, error)
- type Name
- type RedisSource
- func (r *RedisSource) BatchGet(ctx context.Context, bucketKeys []string) (map[string]time.Time, error)
- func (r *RedisSource) BatchIncrement(ctx context.Context, buckets map[string]increment) error
- func (r *RedisSource) BatchSet(ctx context.Context, buckets map[string]time.Time) error
- func (r *RedisSource) BatchSetNotExisting(ctx context.Context, buckets map[string]time.Time) (map[string]bool, error)
- func (r *RedisSource) Delete(ctx context.Context, bucketKey string) error
- func (r *RedisSource) Get(ctx context.Context, bucketKey string) (time.Time, error)
- func (r *RedisSource) Ping(ctx context.Context) error
- type Source
- type Transaction
- type TransactionBuilder
- func (builder *TransactionBuilder) CertificatesPerDomainSpendOnlyTransactions(regId int64, orderDomains []string) ([]Transaction, error)
- func (builder *TransactionBuilder) CertificatesPerFQDNSetSpendOnlyTransaction(orderNames []string) (Transaction, error)
- func (builder *TransactionBuilder) FailedAuthorizationsForPausingPerDomainPerAccountTransaction(regId int64, orderDomain string) (Transaction, error)
- func (builder *TransactionBuilder) FailedAuthorizationsPerDomainPerAccountCheckOnlyTransactions(regId int64, orderDomains []string) ([]Transaction, error)
- func (builder *TransactionBuilder) FailedAuthorizationsPerDomainPerAccountSpendOnlyTransaction(regId int64, orderDomain string) (Transaction, error)
- func (builder *TransactionBuilder) NewAccountLimitTransactions(ip net.IP) ([]Transaction, error)
- func (builder *TransactionBuilder) NewOrderLimitTransactions(regId int64, names []string, isRenewal bool) ([]Transaction, error)
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 ¶
var ErrBucketNotFound = fmt.Errorf("bucket not found")
ErrBucketNotFound indicates that the bucket was not found.
var ErrInvalidCost = fmt.Errorf("invalid cost, must be >= 0")
ErrInvalidCost indicates that the cost specified was < 0.
var ErrInvalidCostOverLimit = fmt.Errorf("invalid cost, must be <= limit.Burst")
ErrInvalidCostOverLimit indicates that the cost specified was > limit.Burst.
Functions ¶
func FQDNsToETLDsPlusOne ¶
FQDNsToETLDsPlusOne transforms a list of FQDNs into a list of eTLD+1's for the CertificatesPerDomain limit. It also de-duplicates the output domains. Exact public suffix matches are included.
func NewInmemSource ¶
func NewInmemSource() *inmem
func NewRegIdDomainBucketKey ¶
NewRegIdDomainBucketKey validates and returns a bucketKey for limits that use the 'enum:regId:domain' bucket key format. This function is exported for use in ra.resetAccountPausingLimit.
Types ¶
type Decision ¶
type Decision struct {
// contains filtered or unexported fields
}
Decision represents the result of a rate limit check or spend operation. To check the result of a *Decision, call the Result() method.
type LimitConfig ¶
type LimitConfig struct { // Burst specifies maximum concurrent allowed requests at any given time. It // must be greater than zero. Burst int64 // Count is the number of requests allowed per period. It must be greater // than zero. Count int64 // Period is the duration of time in which the count (of requests) is // allowed. It must be greater than zero. Period config.Duration }
LimitConfig defines the exportable configuration for a rate limit or a rate limit override, without a `limit`'s internal fields.
The zero value of this struct is invalid, because some of the fields must be greater than zero.
type LimitConfigs ¶
type LimitConfigs map[string]*LimitConfig
type Limiter ¶
type Limiter struct {
// contains filtered or unexported fields
}
Limiter provides a high-level interface for rate limiting requests by utilizing a token bucket-style approach.
func NewLimiter ¶
func NewLimiter(clk clock.Clock, source Source, stats prometheus.Registerer) (*Limiter, error)
NewLimiter returns a new *Limiter. The provided source must be safe for concurrent use.
func (*Limiter) BatchRefund ¶
BatchRefund attempts to refund all or some of the costs to the provided buckets' capacities. Non-existent buckets will NOT be initialized. The new bucket state is persisted to the underlying datastore, if applicable, before returning. Spend-only Transactions are assumed to be refundable. Check-only Transactions are never refunded. The returned *Decision represents the strictest of all *Decisions reached in the batch.
func (*Limiter) BatchSpend ¶
BatchSpend attempts to deduct the costs from the provided buckets' capacities. If applicable, new bucket states are persisted to the underlying datastore before returning. Non-existent buckets will be initialized WITH the cost factored into the initial state. The returned *Decision represents the strictest of all *Decisions reached in the batch.
func (*Limiter) Check ¶
Check DOES NOT deduct the cost of the request from the provided bucket's capacity. The returned *Decision indicates whether the capacity exists to satisfy the cost and represents the hypothetical state of the bucket IF the cost WERE to be deducted. If no bucket exists it will NOT be created. No state is persisted to the underlying datastore.
func (*Limiter) Refund ¶
Refund attempts to refund all of the cost to the capacity of the specified bucket. The returned *Decision indicates whether the refund was successful and represents the current state of the bucket. The new bucket state is persisted to the underlying datastore, if applicable, before returning. If no bucket exists it will NOT be created. Spend-only Transactions are assumed to be refundable. Check-only Transactions are never refunded.
Note: The amount refunded cannot cause the bucket to exceed its maximum capacity. 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 ¶
Reset resets the specified bucket to its maximum capacity. The new bucket state is persisted to the underlying datastore before returning.
func (*Limiter) Spend ¶
Spend attempts to deduct the cost from the provided bucket's capacity. The returned *Decision indicates whether the capacity existed to satisfy the cost and represents the current state of the bucket. If no bucket exists it WILL be created WITH the cost factored into its initial state. The new bucket state is persisted to the underlying datastore, if applicable, before returning.
type Name ¶
type Name int
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 or remove a limit Name, you MUST update:
- the string representation of the Name in nameToString,
- the validators for that name in validateIdForName(),
- the transaction constructors for that name in bucket.go, and
- the Subscriber facing error message in ErrForDecision().
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 // FailedAuthorizationsPerDomainPerAccount uses two different bucket keys // depending on the context: // - When referenced in an overrides file: uses bucket key 'enum:regId', // where regId is the ACME registration Id of the account. // - When referenced in a transaction: uses bucket key 'enum:regId:domain', // where regId is the ACME registration Id of the account and domain is a // domain name in the certificate. FailedAuthorizationsPerDomainPerAccount // CertificatesPerDomain uses bucket key 'enum:domain', where domain is a // domain name in the certificate. CertificatesPerDomain // CertificatesPerDomainPerAccount is only used for per-account overrides to // the CertificatesPerDomain rate limit. If this limit is referenced in the // default limits file, it will be ignored. It uses two different bucket // keys depending on the context: // - When referenced in an overrides file: uses bucket key 'enum:regId', // where regId is the ACME registration Id of the account. // - When referenced in a transaction: uses bucket key 'enum:regId:domain', // where regId is the ACME registration Id of the account and domain is a // domain name in the certificate. // // When overrides to the CertificatesPerDomainPerAccount are configured for a // subscriber, the cost: // - MUST be consumed from each CertificatesPerDomainPerAccount bucket and // - SHOULD be consumed from each CertificatesPerDomain bucket, if possible. CertificatesPerDomainPerAccount // CertificatesPerFQDNSet uses bucket key 'enum:fqdnSet', where fqdnSet is a // hashed set of unique eTLD+1 domain names in the certificate. // // Note: When this is referenced in an overrides file, the fqdnSet MUST be // passed as a comma-separated list of domain names. CertificatesPerFQDNSet // FailedAuthorizationsForPausingPerDomainPerAccount is similar to // FailedAuthorizationsPerDomainPerAccount in that it uses two different // bucket keys depending on the context: // - When referenced in an overrides file: uses bucket key 'enum:regId', // where regId is the ACME registration Id of the account. // - When referenced in a transaction: uses bucket key 'enum:regId:domain', // where regId is the ACME registration Id of the account and domain is a // domain name in the certificate. FailedAuthorizationsForPausingPerDomainPerAccount )
func (Name) EnumString ¶
EnumString returns the string representation of the Name enumeration.
type RedisSource ¶
type RedisSource struct {
// contains filtered or unexported fields
}
RedisSource is a ratelimits source backed by sharded Redis.
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) BatchGet ¶
func (r *RedisSource) BatchGet(ctx context.Context, bucketKeys []string) (map[string]time.Time, error)
BatchGet retrieves the TATs at the specified bucketKeys using a pipelined Redis Transaction in order to reduce the number of round-trips to each Redis shard. If a bucketKey does not exist, it WILL NOT be included in the returned map.
func (*RedisSource) BatchIncrement ¶
func (r *RedisSource) BatchIncrement(ctx context.Context, buckets map[string]increment) error
BatchIncrement updates TATs for the specified bucketKeys using a pipelined Redis Transaction in order to reduce the number of round-trips to each Redis shard.
func (*RedisSource) BatchSet ¶
BatchSet stores TATs at the specified bucketKeys using a pipelined Redis Transaction in order to reduce the number of round-trips to each Redis shard.
func (*RedisSource) BatchSetNotExisting ¶
func (r *RedisSource) BatchSetNotExisting(ctx context.Context, buckets map[string]time.Time) (map[string]bool, error)
BatchSetNotExisting attempts to set TATs for the specified bucketKeys if they do not already exist. Returns a map indicating which keys already existed.
func (*RedisSource) Delete ¶
func (r *RedisSource) Delete(ctx context.Context, bucketKey string) error
Delete deletes the TAT at the specified bucketKey ('name:id'). A nil return value does not indicate that the bucketKey existed.
type Source ¶
type Source interface { // BatchSet stores the TATs at the specified bucketKeys (formatted as // 'name:id'). Implementations MUST ensure non-blocking operations by // either: // a) applying a deadline or timeout to the context WITHIN the method, or // b) guaranteeing the operation will not block indefinitely (e.g. via // the underlying storage client implementation). BatchSet(ctx context.Context, bucketKeys map[string]time.Time) error // BatchSetNotExisting attempts to set TATs for the specified bucketKeys if // they do not already exist. Returns a map indicating which keys already // exist. BatchSetNotExisting(ctx context.Context, buckets map[string]time.Time) (map[string]bool, error) // BatchIncrement updates the TATs for the specified bucketKeys, similar to // BatchSet. Implementations MUST ensure non-blocking operations by either: // a) applying a deadline or timeout to the context WITHIN the method, or // b) guaranteeing the operation will not block indefinitely (e.g. via // the underlying storage client implementation). BatchIncrement(ctx context.Context, buckets map[string]increment) error // Get retrieves the TAT associated with the specified bucketKey (formatted // as 'name:id'). Implementations MUST ensure non-blocking operations by // either: // a) applying a deadline or timeout to the context WITHIN the method, or // b) guaranteeing the operation will not block indefinitely (e.g. via // the underlying storage client implementation). Get(ctx context.Context, bucketKey string) (time.Time, error) // BatchGet retrieves the TATs associated with the specified bucketKeys // (formatted as 'name:id'). Implementations MUST ensure non-blocking // operations by either: // a) applying a deadline or timeout to the context WITHIN the method, or // b) guaranteeing the operation will not block indefinitely (e.g. via // the underlying storage client implementation). BatchGet(ctx context.Context, bucketKeys []string) (map[string]time.Time, error) // Delete removes the TAT associated with the specified bucketKey (formatted // as 'name:id'). Implementations MUST ensure non-blocking operations by // either: // a) applying a deadline or timeout to the context WITHIN the method, or // b) guaranteeing the operation will not block indefinitely (e.g. via // the underlying storage client implementation). Delete(ctx context.Context, bucketKey string) error }
Source is an interface for creating and modifying TATs.
type Transaction ¶
type Transaction struct {
// contains filtered or unexported fields
}
Transaction represents a single rate limit operation. It includes a bucketKey, which combines the specific rate limit enum with a unique identifier to form the key where the state of the "bucket" can be referenced or stored by the Limiter, the rate limit being enforced, a cost which MUST be >= 0, and check/spend fields, which indicate how the Transaction should be processed. The following are acceptable combinations of check/spend:
- check-and-spend: when check and spend are both true, the cost will be checked against the bucket's capacity and spent/refunded, when possible.
- check-only: when only check is true, the cost will be checked against the bucket's capacity, but will never be spent/refunded.
- spend-only: when only spend is true, spending is best-effort. Regardless of the bucket's capacity, the transaction will be considered "allowed".
- allow-only: when neither check nor spend are true, the transaction will be considered "allowed" regardless of the bucket's capacity. This is useful for limits that are disabled.
The zero value of Transaction is an allow-only transaction and is valid even if it would fail validateTransaction (for instance because cost and burst are zero).
type TransactionBuilder ¶
type TransactionBuilder struct {
// contains filtered or unexported fields
}
TransactionBuilder is used to build Transactions for various rate limits. Each rate limit has a corresponding method that returns a Transaction for that limit. Call NewTransactionBuilder to create a new *TransactionBuilder.
func NewTransactionBuilder ¶
func NewTransactionBuilder(defaults LimitConfigs) (*TransactionBuilder, error)
NewTransactionBuilder returns a new *TransactionBuilder. The provided defaults map is expected to contain default limit data. Overrides are not supported. Defaults is required.
func NewTransactionBuilderFromFiles ¶
func NewTransactionBuilderFromFiles(defaults, overrides string) (*TransactionBuilder, error)
NewTransactionBuilderFromFiles returns a new *TransactionBuilder. The provided defaults and overrides paths are expected to be paths to YAML files that contain the default and override limits, respectively. Overrides is optional, defaults is required.
func (*TransactionBuilder) CertificatesPerDomainSpendOnlyTransactions ¶
func (builder *TransactionBuilder) CertificatesPerDomainSpendOnlyTransactions(regId int64, orderDomains []string) ([]Transaction, error)
CertificatesPerDomainSpendOnlyTransactions returns a slice of Transactions for the specified order domain names. It returns an error if any domain names are invalid. If a CertificatesPerDomainPerAccount override is configured, it generates two types of Transactions:
- A spend-only Transaction for each per-account, per-domain bucket, which enforces the limit on certificates issued per domain for each account.
- A spend-only Transaction for each per-domain bucket, which enforces the global limit on certificates issued per domain.
If no CertificatesPerDomainPerAccount override is present, it returns a spend-only Transaction for each global per-domain bucket. This method should be used for spending capacity, when a certificate is issued.
Precondition: orderDomains must all pass policy.WellFormedDomainNames.
func (*TransactionBuilder) CertificatesPerFQDNSetSpendOnlyTransaction ¶
func (builder *TransactionBuilder) CertificatesPerFQDNSetSpendOnlyTransaction(orderNames []string) (Transaction, error)
CertificatesPerFQDNSetSpendOnlyTransaction returns a spend-only Transaction for the provided order domain names. This method should only be used for spending capacity, when a certificate is issued.
func (*TransactionBuilder) FailedAuthorizationsForPausingPerDomainPerAccountTransaction ¶
func (builder *TransactionBuilder) FailedAuthorizationsForPausingPerDomainPerAccountTransaction(regId int64, orderDomain string) (Transaction, error)
FailedAuthorizationsForPausingPerDomainPerAccountTransaction returns a Transaction for the provided order domain name. An error is returned if the order domain name is invalid. This method should be used for spending capacity, as a result of a failed authorization.
func (*TransactionBuilder) FailedAuthorizationsPerDomainPerAccountCheckOnlyTransactions ¶
func (builder *TransactionBuilder) FailedAuthorizationsPerDomainPerAccountCheckOnlyTransactions(regId int64, orderDomains []string) ([]Transaction, error)
FailedAuthorizationsPerDomainPerAccountCheckOnlyTransactions returns a slice of Transactions for the provided order domain names. An error is returned if any of the order domain names are invalid. This method should be used for checking capacity, before allowing more authorizations to be created.
Precondition: len(orderDomains) < maxNames.
func (*TransactionBuilder) FailedAuthorizationsPerDomainPerAccountSpendOnlyTransaction ¶
func (builder *TransactionBuilder) FailedAuthorizationsPerDomainPerAccountSpendOnlyTransaction(regId int64, orderDomain string) (Transaction, error)
FailedAuthorizationsPerDomainPerAccountSpendOnlyTransaction returns a spend- only Transaction for the provided order domain name. An error is returned if the order domain name is invalid. This method should be used for spending capacity, as a result of a failed authorization.
func (*TransactionBuilder) NewAccountLimitTransactions ¶
func (builder *TransactionBuilder) NewAccountLimitTransactions(ip net.IP) ([]Transaction, error)
NewAccountLimitTransactions takes in an IP address from a new-account request and returns the set of rate limit transactions that should be evaluated before allowing the request to proceed.
func (*TransactionBuilder) NewOrderLimitTransactions ¶
func (builder *TransactionBuilder) NewOrderLimitTransactions(regId int64, names []string, isRenewal bool) ([]Transaction, error)
NewOrderLimitTransactions takes in values from a new-order request and returns the set of rate limit transactions that should be evaluated before allowing the request to proceed.
Precondition: names must be a list of DNS names that all pass policy.WellFormedDomainNames.