c82b14b0c4
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.
83 lines
2.1 KiB
Go
83 lines
2.1 KiB
Go
package config
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/hashicorp/nomad/helper"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
// TestLimits_Defaults asserts the default limits are valid.
|
|
func TestLimits_Defaults(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
l := DefaultLimits()
|
|
d, err := time.ParseDuration(l.HTTPSHandshakeTimeout)
|
|
require.NoError(t, err)
|
|
require.True(t, d > 0)
|
|
|
|
d, err = time.ParseDuration(l.RPCHandshakeTimeout)
|
|
require.NoError(t, err)
|
|
require.True(t, d > 0)
|
|
}
|
|
|
|
// TestLimits_Copy asserts Limits structs are deep copied.
|
|
func TestLimits_Copy(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
o := DefaultLimits()
|
|
c := o.Copy()
|
|
|
|
// Assert changes to copy are not propagated to the original
|
|
c.HTTPSHandshakeTimeout = "1s"
|
|
c.HTTPMaxConnsPerClient = helper.IntToPtr(50)
|
|
c.RPCHandshakeTimeout = "1s"
|
|
c.RPCMaxConnsPerClient = helper.IntToPtr(50)
|
|
|
|
require.NotEqual(t, c.HTTPSHandshakeTimeout, o.HTTPSHandshakeTimeout)
|
|
|
|
// Pointers should be different
|
|
require.True(t, c.HTTPMaxConnsPerClient != o.HTTPMaxConnsPerClient)
|
|
|
|
require.NotEqual(t, c.HTTPMaxConnsPerClient, o.HTTPMaxConnsPerClient)
|
|
require.NotEqual(t, c.RPCHandshakeTimeout, o.RPCHandshakeTimeout)
|
|
|
|
// Pointers should be different
|
|
require.True(t, c.RPCMaxConnsPerClient != o.RPCMaxConnsPerClient)
|
|
|
|
require.NotEqual(t, c.RPCMaxConnsPerClient, o.RPCMaxConnsPerClient)
|
|
}
|
|
|
|
// TestLimits_Merge asserts non-zero fields from the method argument take
|
|
// precedence over the existing limits.
|
|
func TestLimits_Merge(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
l := Limits{}
|
|
o := DefaultLimits()
|
|
m := l.Merge(o)
|
|
|
|
// Operands should not change
|
|
require.Equal(t, Limits{}, l)
|
|
require.Equal(t, DefaultLimits(), o)
|
|
|
|
// m == o
|
|
require.Equal(t, m, DefaultLimits())
|
|
|
|
o.HTTPSHandshakeTimeout = "10s"
|
|
m2 := m.Merge(o)
|
|
|
|
// Operands should not change
|
|
require.Equal(t, m, DefaultLimits())
|
|
|
|
// Use short struct initialization style so it fails to compile if
|
|
// fields are added
|
|
expected := Limits{"10s", helper.IntToPtr(100), "5s", helper.IntToPtr(100)}
|
|
require.Equal(t, expected, m2)
|
|
|
|
// Mergin in 0 values should not change anything
|
|
m3 := m2.Merge(Limits{})
|
|
require.Equal(t, m2, m3)
|
|
}
|