ratelimit

package
v0.20.12 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 19, 2024 License: Apache-2.0, MIT Imports: 21 Imported by: 8

Documentation

Overview

Package ratelimit implements rate limiting functionality for the proxy.

It provides per process rate limiting. It can be configured globally, or based on routes. Rate limiting can be lookuped based on HTTP headers for example X-Forwarded-For or Authorization.

Lookuper Type - SameBucketLookuper

This lookuper will use a static string to point always to the same bucket. This means all requests are counted the same.

Lookuper Type - HeaderLookuper

This lookuper will use the content of the the specified header to calculate rate limiting.

Lookuper Type - XForwardedForLookuper

This lookuper will use the remote IP of the origin request to calculate rate limiting. If there is no such header it will use the remote IP of the request. This is the default Lookuper and may be the one most users want to use.

Usage

When imported as a package, the Registry can be used to hold the rate limiters and their settings. On a higher level, rate limiter settings can be simply passed to skipper as part of the skipper.Options object, or defined as command line flags.

The following command starts skipper with default X-Forwarded-For Lookuper, that will start to rate limit after 5 requests within 60s from the same client

% skipper -ratelimits type=client,max-hits=5,time-window=60s

The following configuration will rate limit /foo after 2 requests within 90s from the same requester and all other requests after 20 requests within 60s from the same client

% cat ratelimit.eskip
foo: Path("/foo") -> clientRatelimit(2,"1m30s") -> "http://www.example.org/foo"
rest: * -> clientRatelimit(20,"1m") -> "http://www.example.net/"
% skipper -enable-ratelimits -routes-file=ratelimit.eskip

The following configuration will rate limit requests after 100 requests within 1 minute with the same Authorization Header

% cat ratelimit-auth.eskip
all: * -> clientRatelimit(100,"1m","Authorization") -> "http://www.example.org/"
% skipper -enable-ratelimits -routes-file=ratelimit-auth.eskip

The following configuration will rate limit requests to /login after 10 requests summed across all skipper peers within one hour from the same requester.

% cat ratelimit.eskip
foo: Path("/login") -> clientRatelimit(10,"1h") -> "http://www.example.org/login"
rest: * -> "http://www.example.net/"
% skipper -enable-ratelimits -routes-file=ratelimit.eskip -enable-swarm

Rate limiter settings can be applied globally via command line flags or within routing settings.

Settings - Type

Defines the type of the rate limiter. There are types that only use local state information and others that use cluster information using swarm.Swarm or redis ring shards to exchange information. Types that use instance local information are ServiceRatelimit to be used to protect backends and ClientRatelimit to protect from too chatty clients. Types that use cluster information are ClusterServiceRatelimit to be used to protect backends and and ClusterClientRatelimit to protect from too chatty clients. ClusterClientRatelimit using swarm should be carefully tested with your current memory settings (about 15MB for 100.000 attackers per filter), but the use cases are to protect from login attacks, user enumeration or DDoS attacks. ClusterServiceRatelimit and ClusterClientRatelimit using redis ring shards should be carefully tested, because of redis. Redis ring based cluster ratelimits should not create a significant memory footprint for skipper instances, but might create load to redis.

Settings - MaxHits

Defines the maximum number of requests per user within a TimeWindow.

Settings - TimeWindow

Defines the time window until rate limits will be enforced, if maximum number of requests are exceeded. This is defined as a string representation of Go's time.Duration, e.g. 1m30s.

Settings - Lookuper

Defines an optional configuration to choose which Header should be used to group client requests. It accepts any header, for example "Authorization".

Settings - Group

Defines the ratelimit group, which can be the same for different routes, if you want to have one ratelimiter spanning more than one route. Make sure your settings are the same for the whole group. In case of different settings for the same group the behavior is undefined and could toggle between different configurations.

HTTP Response

In case of rate limiting, the HTTP response status will be 429 Too Many Requests and two headers will be set.

One which shows the maximum requests per hour:

X-Rate-Limit: 6000

And another indicating how long (in seconds) to wait before making a new request:

Retry-After: 3600

Both are based on RFC 6585.

Registry

The active rate limiters are stored in a registry. They are created based on routes or command line flags. The registry synchronizes access to the shared rate limiters. A registry has default settings that it will apply and that it will use the disable rate limiter in case it's not defined in the configuration or not global enabled.

Index

Constants

View Source
const (
	// Header is
	Header = "X-Rate-Limit"

	// RetryHeader is name of the header which will be used to indicate how
	// long a client should wait before making a new request
	RetryAfterHeader = "Retry-After"

	// Deprecated, use filters.RatelimitName instead
	ServiceRatelimitName = filters.RatelimitName

	// LocalRatelimitName *DEPRECATED*, use ClientRatelimitName instead
	LocalRatelimitName = "localRatelimit"

	// Deprecated, use filters.ClientRatelimitName instead
	ClientRatelimitName = filters.ClientRatelimitName

	// Deprecated, use filters.ClusterRatelimitName instead
	ClusterServiceRatelimitName = filters.ClusterRatelimitName

	// Deprecated, use filters.ClusterClientRatelimitName instead
	ClusterClientRatelimitName = filters.ClusterClientRatelimitName

	// Deprecated, use filters.DisableRatelimitName instead
	DisableRatelimitName = filters.DisableRatelimitName

	// Deprecated, use filters.UnknownRatelimitName instead
	UknownRatelimitName = filters.UnknownRatelimitName
)
View Source
const (
	DefaultMaxhits       = 20
	DefaultTimeWindow    = 1 * time.Second
	DefaultCleanInterval = 60 * time.Second
)

Variables

This section is empty.

Functions

func Headers added in v0.11.190

func Headers(maxHits int, timeWindow time.Duration, retryAfter int) http.Header

Types

type ClusterLeakyBucket added in v0.13.208

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

func NewClusterLeakyBucket added in v0.13.208

func NewClusterLeakyBucket(r *Registry, capacity int, emission time.Duration) *ClusterLeakyBucket

NewClusterLeakyBucket creates a class of leaky buckets of a given capacity and emission. Emission is the reciprocal of the leak rate and equals the time to leak one unit.

The leaky bucket is an algorithm based on an analogy of how a bucket with a constant leak will overflow if either the average rate at which water is poured in exceeds the rate at which the bucket leaks or if more water than the capacity of the bucket is poured in all at once. See https://en.wikipedia.org/wiki/Leaky_bucket

func (*ClusterLeakyBucket) Add added in v0.13.208

func (b *ClusterLeakyBucket) Add(ctx context.Context, label string, increment int) (added bool, retry time.Duration, err error)

Add adds an increment amount to the bucket identified by the label. It returns true if the amount was successfully added to the bucket or a time to wait for the next attempt. It also returns any error occurred during the attempt.

type HeaderLookuper added in v0.10.153

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

HeaderLookuper implements Lookuper interface and will select a bucket by Authorization header.

func NewHeaderLookuper added in v0.10.153

func NewHeaderLookuper(k string) HeaderLookuper

NewHeaderLookuper returns HeaderLookuper configured to lookup header named k

func (HeaderLookuper) Lookup added in v0.10.153

func (h HeaderLookuper) Lookup(req *http.Request) string

Lookup returns the content of the Authorization header.

func (HeaderLookuper) String added in v0.10.234

func (h HeaderLookuper) String() string

type Lookuper

type Lookuper interface {
	// Lookup is used to get the string which is used to define
	// how the bucket of a ratelimiter looks like, which is used
	// to decide to ratelimit or not. For example you can use the
	// X-Forwarded-For Header if you want to rate limit based on
	// source ip behind a proxy/loadbalancer or the Authorization
	// Header for request per token or user.
	Lookup(*http.Request) string
}

Lookuper makes it possible to be more flexible for ratelimiting.

func NewRoundRobinLookuper added in v0.13.137

func NewRoundRobinLookuper(n uint64) Lookuper

NewRoundRobinLookuper returns a RoundRobinLookuper.

type Lookupers added in v0.10.234

type Lookupers []Lookuper

Lookupers is a slice of Lookuper, required to get a hashable member in the TupleLookuper.

type Ratelimit

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

Ratelimit is a proxy object that delegates to limiter implementations and stores settings for the ratelimiter

func (*Ratelimit) Allow

func (l *Ratelimit) Allow(ctx context.Context, s string) bool

Allow is used to get a decision if you should allow the call with context, e.g. to support OpenTracing.

func (*Ratelimit) Close

func (l *Ratelimit) Close()

Close will stop any cleanup goroutines in underlying limiter implementation.

func (*Ratelimit) Delta added in v0.10.113

func (l *Ratelimit) Delta(s string) time.Duration

func (*Ratelimit) Resize added in v0.10.113

func (l *Ratelimit) Resize(s string, i int)

func (*Ratelimit) RetryAfter added in v0.10.89

func (l *Ratelimit) RetryAfter(s string) int

RetryAfter informs how many seconds to wait for the next request

type RatelimitType added in v0.10.113

type RatelimitType int

RatelimitType defines the type of the used ratelimit

const (
	// NoRatelimit is not used
	NoRatelimit RatelimitType = iota

	// ServiceRatelimit is used to have a simple rate limit for a
	// backend service, which is calculated and measured within
	// each instance
	ServiceRatelimit

	// LocalRatelimit *DEPRECATED* will be replaced by ClientRatelimit
	LocalRatelimit

	// ClientRatelimit is used to have a simple local rate limit
	// per user for a backend, which is calculated and measured
	// within each instance. One filter consumes memory calculated
	// by the following formular, where N is the number of
	// individual clients put into the same bucket, M the maximum
	// number of requests allowed:
	//
	//    memory = N * M * 15 byte
	//
	// For example /login protection 100.000 attacker, 10 requests
	// for 1 hour will use roughly 14.6 MB.
	ClientRatelimit

	// ClusterServiceRatelimit is used to calculate a rate limit
	// for a whole skipper fleet for a backend service, needs
	// swarm to be enabled with -enable-swarm.
	ClusterServiceRatelimit

	// ClusterClientRatelimit is used to calculate a rate limit
	// for a whole skipper fleet per user for a backend, needs
	// swarm to be enabled with -enable-swarm. In case of redis it
	// will not consume more memory.
	// In case of swim based cluster ratelimit, one filter
	// consumes memory calculated by the following formular, where
	// N is the number of individual clients put into the same
	// bucket, M the maximum number of requests allowed, S the
	// number of skipper peers:
	//
	//    memory = N * M * 15 + S * len(peername)
	//
	// For example /login protection 100.000 attacker, 10 requests
	// for 1 hour, 100 skipper peers with each a name of 8
	// characters will use roughly 14.7 MB.
	ClusterClientRatelimit

	// DisableRatelimit is used to disable rate limit
	DisableRatelimit
)

func (RatelimitType) String added in v0.10.153

func (rt RatelimitType) String() string

func (*RatelimitType) UnmarshalYAML added in v0.11.0

func (rt *RatelimitType) UnmarshalYAML(unmarshal func(interface{}) error) error

type Registry

type Registry struct {
	sync.Mutex
	// contains filtered or unexported fields
}

Registry objects hold the active ratelimiters, ensure synchronized access to them, apply default settings and recycle the idle ratelimiters.

func NewRegistry

func NewRegistry(settings ...Settings) *Registry

NewRegistry initializes a registry with the provided default settings.

func NewSwarmRegistry added in v0.10.113

func NewSwarmRegistry(swarm Swarmer, ro *net.RedisOptions, settings ...Settings) *Registry

NewSwarmRegistry initializes a registry with an optional swarm and the provided default settings. If swarm is nil, clusterRatelimits will be replaced by voidRatelimit, which is a noop limiter implementation.

func (*Registry) Check

func (r *Registry) Check(req *http.Request) (Settings, int)

Check returns Settings used and the retry-after duration in case of request is ratelimitted. Otherwise return the Settings and 0. It is only used in the global ratelimit facility.

func (*Registry) Close added in v0.10.234

func (r *Registry) Close()

Close teardown Registry and dependent resources

func (*Registry) Get

func (r *Registry) Get(s Settings) *Ratelimit

Get returns a Ratelimit instance for provided Settings

type RoundRobinLookuper added in v0.13.137

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

RoundRobinLookuper matches one of n buckets selected by round robin algorithm

func (*RoundRobinLookuper) Lookup added in v0.13.137

func (rrl *RoundRobinLookuper) Lookup(*http.Request) string

Lookup will return one of n distinct keys in round robin fashion

func (*RoundRobinLookuper) String added in v0.13.137

func (rrl *RoundRobinLookuper) String() string

type SameBucketLookuper

type SameBucketLookuper struct{}

SameBucketLookuper implements Lookuper interface and will always match to the same bucket.

func NewSameBucketLookuper

func NewSameBucketLookuper() SameBucketLookuper

NewSameBucketLookuper returns a SameBucketLookuper.

func (SameBucketLookuper) Lookup

Lookup will always return "s" to select the same bucket.

func (SameBucketLookuper) String added in v0.10.234

func (SameBucketLookuper) String() string

type Settings

type Settings struct {
	// FailClosed allows to to decide what happens on failures to
	// query the ratelimit. For example redis is down, fail open
	// or fail closed. FailClosed set to true will deny the
	// request and set to true will allow the request. Default is
	// to fail open.
	FailClosed bool `yaml:"fail-closed"`

	// Type of the chosen rate limiter
	Type RatelimitType `yaml:"type"`

	// Lookuper to decide which data to use to identify the same
	// bucket (for example how to lookup the client identifier)
	Lookuper Lookuper `yaml:"-"`

	// MaxHits the maximum number of hits for a time duration
	// allowed in the same bucket.
	MaxHits int `yaml:"max-hits"`

	// TimeWindow is the time duration that is valid for hits to
	// be counted in the rate limit.
	TimeWindow time.Duration `yaml:"time-window"`

	// CleanInterval is the duration old data can expire, because
	// need to cleanup data in for example client ratelimits.
	CleanInterval time.Duration `yaml:"-"`

	// Group is a string to group ratelimiters of Type
	// ClusterServiceRatelimit or ClusterClientRatelimit.
	// A ratelimit group considers all hits to the same group as
	// one target.
	Group string `yaml:"group"`
}

Settings configures the chosen rate limiter

func (Settings) Empty

func (s Settings) Empty() bool

func (Settings) String

func (s Settings) String() string

type Swarmer added in v0.10.113

type Swarmer interface {
	// ShareValue is used to share the local information with its peers.
	ShareValue(string, interface{}) error
	// Values is used to get global information about current rates.
	Values(string) map[string]interface{}
}

Swarmer interface defines the requirement for a Swarm, for use as an exchange method for cluster ratelimits: ratelimit.ClusterServiceRatelimit and ratelimit.ClusterClientRatelimit.

type TupleLookuper added in v0.10.234

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

TupleLookuper implements Lookuper interface and will select a bucket that is defined by all combined Lookupers.

func NewTupleLookuper added in v0.10.234

func NewTupleLookuper(args ...Lookuper) TupleLookuper

NewTupleLookuper returns TupleLookuper configured to lookup the combined result of all given Lookuper

func (TupleLookuper) Lookup added in v0.10.234

func (t TupleLookuper) Lookup(req *http.Request) string

Lookup returns the combined string of all Lookupers part of the tuple

func (TupleLookuper) String added in v0.10.234

func (t TupleLookuper) String() string

type XForwardedForLookuper

type XForwardedForLookuper struct{}

XForwardedForLookuper implements Lookuper interface and will select a bucket by X-Forwarded-For header or clientIP.

func NewXForwardedForLookuper

func NewXForwardedForLookuper() XForwardedForLookuper

NewXForwardedForLookuper returns an empty XForwardedForLookuper

func (XForwardedForLookuper) Lookup

Lookup returns the content of the X-Forwarded-For header or the clientIP if not set.

func (XForwardedForLookuper) String added in v0.10.234

func (XForwardedForLookuper) String() string

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL