authors | state |
---|---|
Anton Repin (github.com/xdire) |
wip |
This RFD Details the aspects of implementation of TCP load balancer up the following minimal requirements:
- A least connections forwarder implementation that tracks the number of connections per upstream.
- A per-client connection rate limiter implementation that tracks the number of client connections.
- Health checking for unhealthy upstreams. Should not be eligible to receive connections until upstream determined to be healthy.
- mTLS authentication to have the server verify identity of the client and client of the server
- A simple authorization scheme that defines what upstreams are available to which clients
- Accept and forward connections to upstreams using library
TCP Proxy Load balancer with mTLS can solve the following set of problems:
- Distribute the load for the domain record using the ruleset enforced by the customer
- Serve as a secure communications Gateway pass-through for the network of services
- Provide access management per client resource scope
Design will discuss following areas:
Balancer provides following components in order of flow and appearance:
- Rate Limiting System
- Per Resource Token Bucket based Rate Limiter
- Per LB β LRU TTL Rate Limiter
- Resource Cache
- map of customer pool records
- Forwarder β backend entity providing management per customer pool
- allows selection of the strategy (if supported more than single)
- provides health check host tracker per pool
- provides ability to add to the pool or remove from the pool
ββββββββββββββββββ ββββββββββββ βββββββββββββ ββββββββββββ ββββββββββββ
β IP LRU TTL β β Resource β β Resource β β Healthy β β No error β
βRate Limit Checkβ β lookup βββββΆ cache β β found βββββββ β β
ββββββββββββββββββ βββββββ²βββββ βββββββ¬ββββββ ββββββ²ββββββ β ββββββ²ββββββ€
β² β β β β β β
β β β β β β ββββββ
β β±ββ² β β±ββ² β β±ββ² β
ββββββββββββ β±ββββ² β β±ββββ² β β±ββββ² β
β β±ββββββ² β β±ββββββ² β β±ββββββ² β
β β±ββββββββ² ββββββΌβββββ βββββββββββββ β±ββββββββ² β β±ββββββββ² β
βββββββββββ βββββββ β β±ββββββββββ² β Rate β β β β±ββββββββββ² ββββββββΆββββββββββ² βββΌββββββββ
ββββΆ Request ββββΆ TLS ββββ΄ββΆβHANDSHAKEββ β limiter βββββΆ Forwarder βββββββββΆβSTRATEGYβββ ββDIAL HOSTββ β Connect β
βββββββ²ββββ β portβ β²ββββββββββ± β check β β β β²ββββββββββ± β²ββββββββββ± β streams β
β βββββββ β²ββββββββ± βββββββββββ β β β²ββββββββ² .β. β²ββββββββ± βββββββββββ
β β²ββββββ± β βββββββ β βββββββββββββ±β βββββ( 2 ) β²ββββββ±
β β²ββββ± ββββ€Cacheββββ βββ΄βββ β²ββββ± β β Ask `β¬' β²ββββ±
β β²ββ± ββ βββββββ ββΌβββ€Peekβ β²ββ± βββ€ next β β²ββ±
β ββββββ βββββββββββββ ββββββ β β host β β
β βFailβ βββ Route βββ β ββ¬ββββββ β
β ββββ¬ββ βββββββββββββ β β .β. β
β β βββββββββββββ β βββ΄βββββ( 1 ) ββββββΌββββββ
β β βββ Route βββ β β Mark `β¬' β Error β
β β βββββββββββββΌββββββββββββββΌβββββ€unhealthyβββββ€ β
β βΌ βββββββββββββ β βββββββββββ ββββββββββββ
β ββββββββββββββ βββ Route βββ β
β β IP LRU TTL β βββββββββββββ β
β βRate Limiterβ βββββββββββββ ββββββΌββββββ
β β Add Record β βββββββββββββ β Unhealthyβ
β ββββββββββββββ β only β
ββββββ΄βββββ β ββββββββββββββββββββββββββββββββ΄βββββββββββ
β Close ββββββββββββββββββββΌββββββββββββββ
βββββββββββ β
β² β
β β
βββββββββββββββββββββββββ
LoadBalancer can be configured on new instance creation with following slice of Services provided at the time of instance creation:
{services: []ServicePool}
Where ServicePool is representing following interface:
type ServicePool interface {
// How each ServicePool identified, CN match
Identity() string
// Port to listen for incoming traffic
Port() int
// Rate of times per time.Duration
RateQuota() (int, time.Duration)
// How many unauthorized attempts before IP cache placement
UnathorizedAttempts() int
// Bring host back in routable healthy state after this amount of validations
HealthCheckValidations() int
// Routes to route
Routes() []Route
}
type Route interface {
// Stores path of the upstream
Path() string
// Provides information if route is active, in case of update
// function can provide false and that will adjust behavior of forwarder
Active() bool
}
As well load balancer will have method AddServicePool(pool ServicePool)
to do following:
- if pool exists for identity, update this pool
- if pool did not exist, spawn all the required items and run routing
βββββββββββββββββββββ
ββββββββββββββββββββββββββββββββ€Forwarder Selector ββββββββββββββββββββββββββββββββ
β βββββββββββββββββββββ β
β βββββββββββββ β
β ββββββββββββββββββββββββββ€ Slice ββββββββββββββββββββββββββ β
β β βββββββββββββ β β
β β βββββββββ βββββββββ βββββββββ β β
β β βββββ€*Route βββββ βββββ€*Route βββββ βββββ€*Route βββββ β β
β β β βββββββββ β β βββββββββ β β βββββββββ β β β
β β βββββββββββββββββ βββββββββββββββββ βββββββββββββββββ β β
β β ββ Address ββ ββ Address ββ ββ Address ββ β β
β β βββββββββββββββββ βββββββββββββββββ βββββββββββββββββ β β
β β βββββββββββββββββ βββββββββββββββββ βββββββββββββββββ β β
β βββββββββΌββΆβ Healthy ββ ββ Healthy ββ ββ Healthy ββββΌββββββ β
β β β βββββββββββββββββ βββββββββββββββββ βββββββββββββββββ β β β
β β β βββββββββββββββββ βββββββββββββββββ βββββββββββββββββ β β β
β βββββββββΌββΆβ Connections ββ ββ Connections ββ ββ Connections ββ β β β
βββ΄β β βββββββββββββββββ βββββββββββββββββ βββββββββββββββββ β ββ΄β β
ββAβ β βββββββββββββββββ βββββββββββββββββ βββββββββββββββββ β βAβ β
ββTββββββββΌββΆβ Active ββ ββ Active ββ ββ Active ββββΌβββββ€Tβ β
ββOβ β βββββββββββββββββ βββββββββββββββββ βββββββββββββββββ β βOβ β
ββMβ β βββββββββββββββββ βββββββββββββββββ βββββββββββββββββ β βMβ β
ββIβ β β βIβ β
ββCβ βββββββββββββββββββββββββββββββ²ββββββββββββββββββββββββββββββββ βCβ β
βββ¬β βββββββ΄βββββββ ββ¬β β
β β ββββββββββββββββ βMark !Activeβ ββββββββββββββββββ β β
β β β β β Append new β β β β β
β ββββ Strategy β βββββββ¬βββββββ βββΆβ Health-watcher ββββ β
β β β ββββββ΄ββββββ β β β β
β ββββββββββββββββ΄βββββ β Update β β ββββββββββββββββββ β
β βββ©βββββ¦βββββΆ Mutex β β βββββββββββ β
β β Each β ββββββ²ββββββ β β Add β β
β βNext()β β ββββββββ€ Route β β
β ββββββββ β βββββββββββ β
βββββββββββββββββββββββββββββββββββββββββΌββββββββββββββββββββββββββββββββββββββββββ
ββββββ΄βββββ
β Update β
β Routes β
βββββββββββ
Forwarder provides the reference to the slice of []*Route
.
Both Strategy
and Health-Watcher
can use the slice and use atomics
to update
each *Route
individually.
In case if []*Route
should change Forwarder will block on the Update Mutex
adding to the
slice and marking entities that should be removed as inactive (as they still can be in reference
with the strategy which will block only on Next() entry).
Updates are rare, this design does not target additional optimizations.
Provided strategy will use the current reference for provide the selection mechanics as a forward iteration loop.
Strategy will block updateMutex
on Next()
but in following way:
func (s Strategy) Next() *Route {
s.fwd.updateMutex.Lock()
s.fwd.updateMutex.Unlock()
// ... rest of the code
// range s.fwd.Routes
}
This will adjust behavior to not pick the stale []*Route
if Update Routes started
to mark the entries and replacing the slice reference.
However, it will try to use the stale list once if it was fetched before Update Routes happened.
Introducing 2 paths to enforce rate limiting per customer pool and for unauthorized connections.
βββββββββββββ
ββββββ€Authorized ββββββ βββββββββββββββββ
β βββββββββββββ β ββββββββββββββββ€ Token Bucket βββββββββββββββββββ
β β β βββββββββββββββββ ββββββββββ β
β ββββββββββββββββ β βββββββ β ββββ€ Params ββββ β
β β Route Pool 1 βββββΌββ€Has 1ββββΆ β ββββββββββ β β
β ββββββββββββββββ β βββββββ β βββ βCAP:10 TOK:10β β
β β β β0β ββ¬β ββββββββββββββββ€ β
β ββββββββββββββββ β βββββββ β β β β ββββ¬βββ¬βββ ββββββββββββββββ€ β
β β Route Pool 2 βββββΌββ€Has 1ββββΆ β1β β βR1βR2βR3β β-3 TOK:7 β β
β ββββββββββββββββ β βββββββ β β β β ββββ΄βββ΄βββ ββββββββββββββββ€ β
β β β β2β β β 0 TOK:7 β β
β ββββββββββββββββ β βββββββ β β β β ββββ¬βββ¬βββ¬βββ ββββββββββββββββ€ β
β β Route Pool N βββββΌββ€Has 1ββββΆ β3β β βR4βR5βR6βR7β β+1 -4 TOK:3 β β
β ββββββββββββββββ β βββββββ β β β β ββββΌβββΌβββΌβββ€ ββββββββββββββββββββ€ β
βββββββββββββββββββββββ β β4β ββββ βR8βR9βR0βR1β βXXββ+1 -4 TOK:0 β β
β β β β ββ ββββΌβββΌβββΌβββ ββββββββββββββββββββ€ β
β β5β β ββ βR2βXXβXXβ β+1 -1 TOK:0 β β
β β β βTββ ββββΌβββΌβββΌβββ ββββββββββββββββ€ β
β β6β βIββ βR3βXXβXXβXXβ β+1 -1 TOK:0 β β
β β β βMββ ββββΌβββΌβββΌβββ€ ββββββββββββββββ€ β
β β7β βEββ βR4βXXβXXβXXβ β+1 -1 TOK:0 β β
β β β β ββ ββββ΄βββ΄βββ΄βββ ββββββββββββββββ€ β
β β8β β ββ β 0 TOK:0 β β
β β β ββββ ββββββββββββββββ€ β
β β9β β β 0 TOK:0 β β
β β β β ββββββββββββββββ€ β
β β0β ββΌβ β 0 TOK:0 β β
β β β β ββββ¬βββ¬βββ ββββββββββββββββ€ β
β β1β β βR4βR5βR6β β+4 -3 TOK:1 β β
β β β β ββββΌβββ΄βββ ββββββββββββββββ€ β
β β2β β βR7β β+1 -1 TOK:1 β β
β βββ β ββββ ββββββββββββββββ β
β βΌ β
ββββββββββββββββββββββββββββββββββββββββββββββββββ
ββββββββββββββ
βββββ€Unauthorizedβββββ ββββββββββββββββ
β ββββββββββββββ β β Not In Cache βββββ
β β ββββββββββββββββ β βββββββββββββββββββ
β ββββββββ β Ξ β βββββ€ LRU+TTL CACHE βββββ
β β IP 1 ββββββββΌβββββ β± β² β β βββββββββββββββββββ β
β ββββββββ β β β± β² ββββΌβΆβββββββ¬βββββ¬βββββββββββ β
β ββββββββ β β β± β² ββIPADDRβ 1 β*time.Timeβ β
β β IP 2 ββββββββΌβββββ€ β± β² βββββββββΌβββββΌβββββββββββ€ β
β ββββββββ β βββββββββββββΆ Check β ββIPADDRβ 4 β*time.Timeβ β
β ββββββββ β β β² Cache β± βββββββββΌβββββΌβββββββββββ€ β
β β IP 3 ββββββββΌβββββ€ β² β± ββIPADDRβ 5 β*time.Timeβ β
β ββββββββ β β β² β± βββββββββ΄ββ²βββ΄βββββββββββ β
β ββββββββ β β β² β± β ββββββ β
β β IP N ββββββββΌβββββ V ββββββΌβββββββββββββββββββββ
β ββββββββ β ββββββββββββββββ β
β β β In Cache β βββββββββββ
ββββββββββββββββββββββ ββββββββββββββββ β β
β βIncrementβ
β β counter β
βββββββββββββββΆβ β
βIf < thanβ
βthresholdβ
β β
βββββββββββ
In this path we provide a Token bucket for each customer pool resource, which will provide the sliding window for the authorized sessions.
For this design we assuming customer is using one balancer services pool as one instance of the service provided and by that design implies rate limiter per one identity of customer pool.
As we must address possibly super-large chunk of IP data to store in memory, in the scope of this library (not distributed) we will provide LRU TTL cache of size, which will update and purge while being called for the records.
- Before TLS Handshake β we will peek in the cache to see if we should perform TLS at all
- After TLS Handshake failed β we will put record in the cache
For managing Unhealthy routes we can create simple service based on concurrent
Min-time-to-next-check
PriorityQueue
. Forwarder can offer *Route
to this scheduler.
- Scheduler will process
*Route
entries whileActive
and!Healthy
. - Scheduler can be configured to do multiple checks before promote to
Healthy
withchecks
param - If
*Route
notActive
anymore, it will not doPush()
afterPop()
Design scheme represents all above properties nicely:
ββββββββββββββββββββββββββ
βββββββββββββββββββββ βββββββββββββββ€ Health Check Routine ββββββββββββββββββ
β SubmitUnhealthy() β β ββββββββββββββββββββββββββ β
βββββββββββββββββββββ β ββββββββββββββββββ β
β β β Priority Queue β β
β β ββββββββββββββββββ β
β β ββββββββββββββββββ β
ββββββββΌβββββββ β β Poll() β β
β ββββββββββ β β βββββββββ¬βββββββββ β
β β *Route β β β β β
β ββββββββββ β β βΌ βββββββββ β
β ββββββββββ β β β± β²β Mutex β β
β β checks β β β β± βββββββββ βββββββββββ β
β ββββββββββ β β βββββββββ βββββββ βpriority β β
β ββββββββββ β β β Empty βββββββββββββPEEK βββββββββββββΆ > β β
β β passed β β β βββββ¬ββββ βββββββ βtime.Now β β
β ββββββββββ β β β β² β± ββββββ¬βββββ β
β ββββββββββ β β β β² β± β β
β β next β β β β β β β
β β check β β ββββββββΌββββββ β ββββββββΌβββββββ β
β ββββββββββ β ββ Await β ββββββΌβββββ β Await β β
ββββββββ¬βββββββ ββ β βtime.Now β β β β
βββββ΄βββββ ββ <-newTask β β > β β <-newTask β β
β Push() β ββ<-ctx.Done β β priorityβ β<-time.After β β
βββββ¬βββββ ββ β ββββββ¬βββββ β <-ctx.Done β β
β ββsleeping=1 β β β β β
βββββββββββββΌβββββββββββββββ ββββββββββββββ ββββββΌβββββ βββββββββββββββ β
β Priority Queue β β β Pop() β β
β β β ββββββ¬βββββ β
β MIN:(time.Now+nextCheck) β β β β
βββββββββββββ¬βββββββββββββββ β ββββββΌβββββ β
β β ββββββββre Dial()ββββββββ β
ββββββββΌβββββββ β β βββββββββββ β β
β β β β βΌ β
β if sleeping β β βΌ βββββββββββ β
β newTask <- β β ββββββββββ β passed β β
β sleeping=0 β β βpassed=0β β == β β
β β β ββββββ¬ββββ β checks β β
βββββββββββββββ β β ββββββ¬βββββ β
β β β β
β ββββββΌββββ ββββββΌβββββ β
β β Push() β β *Route β β
β ββββββββββ β active β β
β βββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Proposed mTLS layer consists of the following scheme:
ββββββββββββββ
βββββββββββββββ£ Load β ββββββββββββββ
β β balancer β β
β ββββββββββββββ β
β β
ββββββββββββ β βββββββββββββββββ β
β β ββββΌβββββββΆβ Accept Cert β β
β CACert βββββββββ ββββββββββββββ β β βββββββββ¬ββββββββ β
β β β β β β β β β
ββββββββββββ ββββββ΄βββ β Client β ββββββββ΄β β βββββββββΌββββββββ β
βProduceβ β Cert ββββ€Connectβ β β Fetch CN β β
ββββββββββββ β TLS ββββββΆβ β βββββββββ β βββββββββββββββββ β
β Client β ββββββ¬βββ β CN=Identityβ β β β
β Frontend β β β β β βββββββββ β
β Access βββββββββ ββββββββββββββ β β β
β key β ββ΄ββββββββΌββββββββ β
ββββββββββββ β Lookup Access β βββββββββββββββββββ β
β Key in Cache β β Dispatch to β β
β of available ββββββΆcorrect Forwarderβ β
β customer pools β βββββββββββββββββββ β
ββ¬ββββββββββββββββ β
ββββββββββββββββββββββββββββββββββββββββββ
Provided to Load Balancer with AddForwarderPool(pool ServicePool)
method where ServicePool
is
the interface for the persistent configuration. ServicePool.Identity()
will be used to ensure
CN
match for authorization.
This design will limit scope of Identity to simple selection of the whole set of the customer pool for such identity.
As the future next steps certificate can be enriched with property like the OU=role
and this will
provide ability to have customer pools to routed for the authentication with additional precision.
For the scope of the project and simplicity of initial implementation we consider following:
- Encryption Key RSA 3072
- Cipher Suites: TLS v1.3 compliant set
- Client Common Name will be pre-filled with Identity Key (for Forwarder selectors)
- TLS v1.3 as default configuration
- mTLS Layer will match CN Access Key to the available Identity Keys in the connection manager at the time of connection, scope should be limited to memory intensive operations for authorizing the connection
- DDoS event might pile up the waiting connections on the port, raising opened descriptors on the
Linux machine to the limits, after which we can lose replica. For this to happen flow of the connections
should exceed processing power for
certificate signature verification + hashMap access time
. - System should be able to drop connection and close descriptors as fast as possible
- Local IP Rate Limiter cache should help to drop unwanted connections for reasonable amount of IP Address
records, taking the LRU structure per IP at ~approx:
- max(4 bytes (16 for str) + 1byte + Time(8 bytes) + DLL(8 + 8 + 8 bytes) + MAP(8 + 8 bytes)) < 128 bytes
- in 1Kb we can carry at least 8 LRU records, 8000 in 1Mb or 800000 in 100Mb, 8M in 1Gb
- capacity of 8M records to be dropped before additional CPU cycles can give some insurance