open-nomad/nomad/structs/config/limits.go
Michael Schurter c82b14b0c4 core: add limits to unauthorized connections
Introduce limits to prevent unauthorized users from exhausting all
ephemeral ports on agents:

 * `{https,rpc}_handshake_timeout`
 * `{http,rpc}_max_conns_per_client`

The handshake timeout closes connections that have not completed the TLS
handshake by the deadline (5s by default). For RPC connections this
timeout also separately applies to first byte being read so RPC
connections with TLS enabled have `rpc_handshake_time * 2` as their
deadline.

The connection limit per client prevents a single remote TCP peer from
exhausting all ephemeral ports. The default is 100, but can be lowered
to a minimum of 26. Since streaming RPC connections create a new TCP
connection (until MultiplexV2 is used), 20 connections are reserved for
Raft and non-streaming RPCs to prevent connection exhaustion due to
streaming RPCs.

All limits are configurable and may be disabled by setting them to `0`.

This also includes a fix that closes connections that attempt to create
TLS RPC connections recursively. While only users with valid mTLS
certificates could perform such an operation, it was added as a
safeguard to prevent programming errors before they could cause resource
exhaustion.
2020-01-30 10:38:25 -08:00

88 lines
2.8 KiB
Go

package config
import "github.com/hashicorp/nomad/helper"
const (
// LimitsNonStreamingConnsPerClient is the number of connections per
// peer to reserve for non-streaming RPC connections. Since streaming
// RPCs require their own TCP connection, they have their own limit
// this amount lower than the overall limit. This reserves a number of
// connections for Raft and other RPCs.
//
// TODO Remove limit once MultiplexV2 is used.
LimitsNonStreamingConnsPerClient = 20
)
// Limits configures timeout limits similar to Consul's limits configuration
// parameters. Limits is the internal version with the fields parsed.
type Limits struct {
// HTTPSHandshakeTimeout is the deadline by which HTTPS TLS handshakes
// must complete.
//
// 0 means no timeout.
HTTPSHandshakeTimeout string `hcl:"https_handshake_timeout"`
// HTTPMaxConnsPerClient is the maximum number of concurrent HTTP
// connections from a single IP address. nil/0 means no limit.
HTTPMaxConnsPerClient *int `hcl:"http_max_conns_per_client"`
// RPCHandshakeTimeout is the deadline by which RPC handshakes must
// complete. The RPC handshake includes the first byte read as well as
// the TLS handshake and subsequent byte read if TLS is enabled.
//
// The deadline is reset after the first byte is read so when TLS is
// enabled RPC connections may take (timeout * 2) to complete.
//
// The RPC handshake timeout only applies to servers. 0 means no
// timeout.
RPCHandshakeTimeout string `hcl:"rpc_handshake_timeout"`
// RPCMaxConnsPerClient is the maximum number of concurrent RPC
// connections from a single IP address. nil/0 means no limit.
RPCMaxConnsPerClient *int `hcl:"rpc_max_conns_per_client"`
}
// DefaultLimits returns the default limits values. User settings should be
// merged into these defaults.
func DefaultLimits() Limits {
return Limits{
HTTPSHandshakeTimeout: "5s",
HTTPMaxConnsPerClient: helper.IntToPtr(100),
RPCHandshakeTimeout: "5s",
RPCMaxConnsPerClient: helper.IntToPtr(100),
}
}
// Merge returns a new Limits where non-empty/nil fields in the argument have
// precedence.
func (l *Limits) Merge(o Limits) Limits {
m := *l
if o.HTTPSHandshakeTimeout != "" {
m.HTTPSHandshakeTimeout = o.HTTPSHandshakeTimeout
}
if o.HTTPMaxConnsPerClient != nil {
m.HTTPMaxConnsPerClient = helper.IntToPtr(*o.HTTPMaxConnsPerClient)
}
if o.RPCHandshakeTimeout != "" {
m.RPCHandshakeTimeout = o.RPCHandshakeTimeout
}
if o.RPCMaxConnsPerClient != nil {
m.RPCMaxConnsPerClient = helper.IntToPtr(*o.RPCMaxConnsPerClient)
}
return m
}
// Copy returns a new deep copy of a Limits struct.
func (l *Limits) Copy() Limits {
c := *l
if l.HTTPMaxConnsPerClient != nil {
c.HTTPMaxConnsPerClient = helper.IntToPtr(*l.HTTPMaxConnsPerClient)
}
if l.RPCMaxConnsPerClient != nil {
c.RPCMaxConnsPerClient = helper.IntToPtr(*l.RPCMaxConnsPerClient)
}
return c
}