tlslimit

package module
v0.0.0-...-8550cd0 Latest Latest
Warning

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

Go to latest
Published: Oct 29, 2022 License: MIT Imports: 7 Imported by: 0

README

PkgGoDev Go Report Card Coverage Status CircleCI

TLSLimit

Limiting the rate of TLS handshakes.

Motivation

When a client send a request, the server must invest some of its precious CPU cycles into responding to that client, In the case of a secured request, the investment is substantial because of the involved cryptography in TLS handshakes.

Application layer rate limits would not prevent CPU overload because they work after TLS termination, same for limiting the number of simultaneous connections because the attacker or user can keep sending new requests using new connections that trigger TLS handshakes again and again.

For example see gitlab production incident caused by TLS handshakes.

TLSLimit is here to help with that, It provides a rate limiter to fewer expensive TLS handshakes, mitigates SSL/TLS exhaustion DDoS attacks, and an overall reduction in required server resources without affecting the overall number of concurrent requests that the server can handle.

Installation

Using tlslimit is easy. First, use go get to install the latest version of the library.

go get github.com/shaj13/tlslimit

Next, include tlslimit in your application:

import (
    "github.com/shaj13/tlslimit"
)

Usage

package main

import (
	"crypto/tls"
	"net/http"
	"time"

	"github.com/shaj13/tlslimit"
)

func main() {
	// Declare the actual callbacks to retrieve the server certificate
	// you don't need both callbacks, choose the one that suits your application needs.
	//
	// Most callers prefer the GetCertificate.

	getCert := func(ci *tls.ClientHelloInfo) (*tls.Certificate, error) {
		// Return actual certificate.
		return nil, nil
	}

	getConfig := func(ci *tls.ClientHelloInfo) (*tls.Config, error) {
		// Return actual config.
		return nil, nil
	}

	// Define a Limiter to enforce TLS rate limiting.
	// To prevent a client from exhausting application resources
	// and mitigates SSL/TLS exhaustion DDoS attacks.
	//
	// For Example Allow 20 TLS handshakes per minute for each client IP.
	lim := tlslimit.NewLimiter(
		tlslimit.WithBursts(20),
		tlslimit.WithLimit(time.Minute),
		tlslimit.WithTLSClientIP(),
		// Use WithGetCertificate or WithGetConfigForClient
		tlslimit.WithGetCertificate(getCert),
		tlslimit.WithGetConfigForClient(getConfig),
	)

	// Tie the Limiter to the tls.Config.
	cfg := &tls.Config{
		// Use GetCertificate or GetConfigForClient
		GetCertificate:     lim.GetCertificate,
		GetConfigForClient: lim.GetConfigForClient,
		MinVersion:         tls.VersionTLS13,
	}

	srv := http.Server{
		TLSConfig: cfg,
	}

	_ = srv.ListenAndServeTLS("", "")
}

Contributing

  1. Fork it
  2. Download your fork to your PC (git clone https://github.com/your_username/tlslimit && cd env)
  3. Create your feature branch (git checkout -b my-new-feature)
  4. Make changes and add them (git add .)
  5. Commit your changes (git commit -m 'Add some feature')
  6. Push to the branch (git push origin my-new-feature)
  7. Create new pull request

License

tlslimit is released under the MIT license. See LICENSE

Documentation

Overview

Package tlslimit provides a rate limiter to fewer expensive TLS handshakes.

Example
package main

import (
	"crypto/tls"
	"net/http"
	"time"

	"github.com/shaj13/tlslimit"
)

func main() {
	// Declare the actual callbacks to retrieve the server certificate
	// you don't need both callbacks, choose the one that suits your application needs.
	//
	// Most callers prefer the GetCertificate.

	getCert := func(ci *tls.ClientHelloInfo) (*tls.Certificate, error) {
		// Return actual certificate.
		return nil, nil
	}

	getConfig := func(ci *tls.ClientHelloInfo) (*tls.Config, error) {
		// Return actual config.
		return nil, nil
	}

	// Define a Limiter to enforce TLS rate limiting.
	// To prevent a client from exhausting application resources
	// and mitigates SSL/TLS exhaustion DDoS attacks.
	//
	// For Example Allow 20 TLS handshakes per minute for each client IP.
	lim := tlslimit.NewLimiter(
		tlslimit.WithBursts(20),
		tlslimit.WithLimit(time.Minute),
		tlslimit.WithTLSClientIP(),
		// Use WithGetCertificate or WithGetConfigForClient
		tlslimit.WithGetCertificate(getCert),
		tlslimit.WithGetConfigForClient(getConfig),
	)

	// Tie the Limiter to the tls.Config.
	cfg := &tls.Config{
		// Use GetCertificate or GetConfigForClient
		GetCertificate:     lim.GetCertificate,
		GetConfigForClient: lim.GetConfigForClient,
		MinVersion:         tls.VersionTLS13,
	}

	srv := http.Server{
		TLSConfig: cfg,
	}

	_ = srv.ListenAndServeTLS("", "")
}

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Limiter

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

Limiter controls how frequently TLS handshakes are allowed to happen. It implements a "token bucket" of size b, initially full and refilled at rate r tokens per duration. See https://en.wikipedia.org/wiki/Token_bucket for more about token buckets.

The zero value is a valid Limiter, but it will reject all TLS handshakes. Use NewLimiter to create non-zero Limiters.

Limiter has two main methods, GetCertificate, and GetConfigForClient suitable to be used in tls.Config

Each of the two methods consumes a single token. If no token is available, It returns error to abort TLS handshake. If client reuse TLS connections (HTTP2), the two methods will not be invoked by "crypto/tls" package then the rate limiting will not be applied. This translates to fewer expensive TLS handshakes, mitigates SSL/TLS exhaustion DDoS attacks, and an overall reduction in required server resources without affecting the overall number of concurrent requests that the server can handle.

Limiter by default applies global rate limiting. Use WithTLSHostname or WithTLSClientIP to apply rate limiting per ip or domain.

func NewLimiter

func NewLimiter(opts ...Option) *Limiter

NewLimiter returns a new Limiter that allows TLS handshakes up to rate r and permits bursts of at most b tokens.

func (*Limiter) GetCertificate

func (lim *Limiter) GetCertificate(ci *tls.ClientHelloInfo) (*tls.Certificate, error)

func (*Limiter) GetConfigForClient

func (lim *Limiter) GetConfigForClient(ci *tls.ClientHelloInfo) (*tls.Config, error)

type Option

type Option interface {
	// contains filtered or unexported methods
}

Option configures Limiter using the functional options paradigm popularized by Rob Pike and Dave Cheney. If you're unfamiliar with this style, see https://commandcenter.blogspot.com/2014/01/self-referential-functions-and-design.html and https://dave.cheney.net/2014/10/17/functional-options-for-friendly-apis.

func WithBursts

func WithBursts(b int) Option

WithBursts defines the maximum number of TLS handshakes. A zero Burst allows no TLS handshakes.

func WithCacheMaxSize

func WithCacheMaxSize(size int) Option

WithCacheMaxSize defines maximum number of cache entries.

func WithGetCertificate

func WithGetCertificate(fn func(*tls.ClientHelloInfo) (*tls.Certificate, error)) Option

WithGetCertificate returns a tls.Certificate based on the given tls.ClientHelloInfo. It will only be called if the Limiter.GetCertificate sat in tls.Config and client TLS handshakes rate limits does not exceeded.

If fn is nil or returns nil, then the TLS handshakes will be aborted.

See the documentation of tls.Config: https://pkg.go.dev/crypto/tls#Config for more information.

func WithGetConfigForClient

func WithGetConfigForClient(fn func(*tls.ClientHelloInfo) (*tls.Config, error)) Option

WithGetConfigForClient returns a tls.Config based on the given tls.ClientHelloInfo. It will only be called if the Limiter.GetConfigForClient sat in tls.Config and client tls handshakes rate limit does not exceeded.

If fn is nil or returns nil, then the original tls.Config will be used.

See the documentation of tls.Config: https://pkg.go.dev/crypto/tls#Config for more information.

func WithLimit

func WithLimit(r time.Duration) Option

WithLimit defines the maximum frequency of TLS handshakes. A zero Limit allows no TLS handshakes.

func WithTLSClientIP

func WithTLSClientIP() Option

WithTLSClientIP apply rate limiting per IP by using *tls.ClientHelloInfo.Conn.RemoteAddr() as a key.

func WithTLSHostname

func WithTLSHostname() Option

WithTLSHostname apply rate limiting per domain by using *tls.ClientHelloInfo.ServerName as a key.

Jump to

Keyboard shortcuts

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