mirror of
https://github.com/cockpit-project/cockpit.git
synced 2026-11-03 21:49:50 -09:00
tls: Limit concurrent connections
Resolves: COCKPIT-1905, RHEL-263070, RHEL-263066
This commit is contained in:
parent
709aac3638
commit
e12c37897d
5 changed files with 79 additions and 4 deletions
|
|
@ -92,6 +92,12 @@ ForwardedForHeader = X-Forwarded-For
|
|||
unauthenticated connections. The probability increases linearly and
|
||||
all connection attempts are refused if the number of unauthenticated
|
||||
connections reaches *full* (60).
|
||||
*MaxConcurrentConnections*::
|
||||
Limits the number of concurrent HTTP connections on the Cockpit port.
|
||||
Connection attempts that exceed this limit are rejected early on
|
||||
without reading a single byte. This can be used during brute-force
|
||||
attacks. It differs from *MaxStartups*, which protects against too
|
||||
many concurrent authentication attempts. Defaults to 500.
|
||||
*AllowUnencrypted*::
|
||||
If true, cockpit will accept unencrypted HTTP connections. Otherwise,
|
||||
it redirects all HTTP connections to HTTPS. Exceptions are connections
|
||||
|
|
|
|||
|
|
@ -94,7 +94,9 @@ main (int argc, char **argv)
|
|||
if (!runtimedir)
|
||||
errx (EXIT_FAILURE, "$RUNTIME_DIRECTORY environment variable must be set to a private directory");
|
||||
|
||||
server_init ("/run/cockpit/wsinstance", runtimedir, arguments.idle_timeout, arguments.port);
|
||||
unsigned int max_connections = cockpit_conf_uint ("WebService", "MaxConcurrentConnections", 500, 10, UINT_MAX);
|
||||
|
||||
server_init ("/run/cockpit/wsinstance", runtimedir, arguments.idle_timeout, arguments.port, max_connections);
|
||||
|
||||
if (!arguments.no_tls)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -37,6 +37,8 @@ static struct {
|
|||
/* rw, protected by mutex */
|
||||
pthread_mutex_t connection_mutex;
|
||||
unsigned int connection_count;
|
||||
unsigned int max_connection_count;
|
||||
bool max_connection_warning_issued;
|
||||
int idle_timerfd;
|
||||
struct itimerspec idle_timeout;
|
||||
} server;
|
||||
|
|
@ -134,6 +136,26 @@ handle_accept (int listen_fd)
|
|||
timerfd_settime (server.idle_timerfd, 0, &zero, NULL);
|
||||
}
|
||||
|
||||
if (server.connection_count >= server.max_connection_count)
|
||||
{
|
||||
if (!server.max_connection_warning_issued)
|
||||
{
|
||||
warnx ("too many connections");
|
||||
server.max_connection_warning_issued = true;
|
||||
}
|
||||
|
||||
pthread_mutex_unlock (&server.connection_mutex);
|
||||
|
||||
// Send a immediate RST to the client to immediately free up
|
||||
// our resources associated with the socket. This results in
|
||||
// a "Connection reset" error on the other end, which is
|
||||
// useful as well.
|
||||
struct linger opt = { .l_onoff = 1, .l_linger = 0 };
|
||||
setsockopt (fd, SOL_SOCKET, SO_LINGER, &opt, sizeof(opt));
|
||||
close (fd);
|
||||
return;
|
||||
}
|
||||
|
||||
server.connection_count++;
|
||||
|
||||
debug (CONNECTION, " -> server.connection_count is now %i", server.connection_count);
|
||||
|
|
@ -186,13 +208,15 @@ void
|
|||
server_init (const char *wsinstance_sockdir,
|
||||
const char *cert_session_dir,
|
||||
int idle_timeout,
|
||||
uint16_t port)
|
||||
uint16_t port,
|
||||
unsigned int max_connection_count)
|
||||
{
|
||||
const char *env_listen_fds;
|
||||
struct epoll_event ev = { .events = EPOLLIN };
|
||||
|
||||
assert (!server.initialized);
|
||||
server.initialized = true;
|
||||
server.max_connection_count = max_connection_count;
|
||||
server.idle_timerfd = -1;
|
||||
|
||||
connection_set_directories (wsinstance_sockdir, cert_session_dir);
|
||||
|
|
|
|||
|
|
@ -14,7 +14,8 @@ void
|
|||
server_init (const char *wsinstance_sockdir,
|
||||
const char *cert_session_dir,
|
||||
int idle_timeout,
|
||||
uint16_t port);
|
||||
uint16_t port,
|
||||
unsigned int max_connections);
|
||||
|
||||
void
|
||||
server_run (void);
|
||||
|
|
|
|||
|
|
@ -61,8 +61,13 @@ typedef struct {
|
|||
const char *client_fingerprint;
|
||||
const char *priority;
|
||||
int expected_pk_algo;
|
||||
unsigned int max_connection_count;
|
||||
} TestFixture;
|
||||
|
||||
static const TestFixture fixture_max_connection_count_10 = {
|
||||
.max_connection_count = 10,
|
||||
};
|
||||
|
||||
static const TestFixture fixture_separate_crt_key = {
|
||||
.certfile = CERTFILE,
|
||||
.keyfile = KEYFILE,
|
||||
|
|
@ -468,8 +473,10 @@ setup (TestCase *tc, gconstpointer data)
|
|||
}
|
||||
close (socket_dir_fd);
|
||||
|
||||
unsigned max_connection_count = (fixture && fixture->max_connection_count != 0) ? fixture->max_connection_count : 500;
|
||||
|
||||
/* Let the kernel assign a port */
|
||||
server_init (tc->ws_socket_dir, tc->runtime_dir, fixture ? fixture->idle_timeout : 0, 0);
|
||||
server_init (tc->ws_socket_dir, tc->runtime_dir, fixture ? fixture->idle_timeout : 0, 0, max_connection_count);
|
||||
|
||||
if (fixture && fixture->certfile)
|
||||
{
|
||||
|
|
@ -671,6 +678,39 @@ test_no_tls_many_parallel (TestCase *tc, gconstpointer data)
|
|||
}
|
||||
}
|
||||
|
||||
static void
|
||||
test_no_tls_too_many_parallel (TestCase *tc, gconstpointer data)
|
||||
{
|
||||
int i;
|
||||
int fds[20];
|
||||
|
||||
const char *request = "GET / HTTP/1.0\r\nHost: localhost\r\n\r\n";
|
||||
|
||||
for (i = 0; i < 20; ++i)
|
||||
{
|
||||
fds[i] = do_connect (tc);
|
||||
g_assert_cmpint (fds[i], >, 0);
|
||||
server_poll_event (50);
|
||||
}
|
||||
|
||||
for (i = 0; i < 20; ++i)
|
||||
{
|
||||
if (i < 10)
|
||||
{
|
||||
/* the first 10 are allowed to connect and will receive a reply */
|
||||
do_request_fd (fds[i], request);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* the rest gets disconnected immediately */
|
||||
int r = write (fds[i], request, strlen (request));
|
||||
g_assert (r == -1 && errno == ECONNRESET);
|
||||
}
|
||||
|
||||
close(fds[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
test_no_tls_redirect (TestCase *tc, gconstpointer data)
|
||||
{
|
||||
|
|
@ -970,6 +1010,8 @@ main (int argc, char *argv[])
|
|||
setup, test_no_tls_many_serial, teardown);
|
||||
g_test_add ("/server/no-tls/many-parallel", TestCase, NULL,
|
||||
setup, test_no_tls_many_parallel, teardown);
|
||||
g_test_add ("/server/no-tls/too-many-parallel", TestCase, &fixture_max_connection_count_10,
|
||||
setup, test_no_tls_too_many_parallel, teardown);
|
||||
g_test_add ("/server/no-tls/redirect", TestCase, NULL,
|
||||
setup, test_no_tls_redirect, teardown);
|
||||
g_test_add ("/server/tls/no-client-cert", TestCase, &fixture_separate_crt_key,
|
||||
|
|
|
|||
Loading…
Reference in a new issue