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
|
unauthenticated connections. The probability increases linearly and
|
||||||
all connection attempts are refused if the number of unauthenticated
|
all connection attempts are refused if the number of unauthenticated
|
||||||
connections reaches *full* (60).
|
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*::
|
*AllowUnencrypted*::
|
||||||
If true, cockpit will accept unencrypted HTTP connections. Otherwise,
|
If true, cockpit will accept unencrypted HTTP connections. Otherwise,
|
||||||
it redirects all HTTP connections to HTTPS. Exceptions are connections
|
it redirects all HTTP connections to HTTPS. Exceptions are connections
|
||||||
|
|
|
||||||
|
|
@ -94,7 +94,9 @@ main (int argc, char **argv)
|
||||||
if (!runtimedir)
|
if (!runtimedir)
|
||||||
errx (EXIT_FAILURE, "$RUNTIME_DIRECTORY environment variable must be set to a private directory");
|
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)
|
if (!arguments.no_tls)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,8 @@ static struct {
|
||||||
/* rw, protected by mutex */
|
/* rw, protected by mutex */
|
||||||
pthread_mutex_t connection_mutex;
|
pthread_mutex_t connection_mutex;
|
||||||
unsigned int connection_count;
|
unsigned int connection_count;
|
||||||
|
unsigned int max_connection_count;
|
||||||
|
bool max_connection_warning_issued;
|
||||||
int idle_timerfd;
|
int idle_timerfd;
|
||||||
struct itimerspec idle_timeout;
|
struct itimerspec idle_timeout;
|
||||||
} server;
|
} server;
|
||||||
|
|
@ -134,6 +136,26 @@ handle_accept (int listen_fd)
|
||||||
timerfd_settime (server.idle_timerfd, 0, &zero, NULL);
|
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++;
|
server.connection_count++;
|
||||||
|
|
||||||
debug (CONNECTION, " -> server.connection_count is now %i", 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,
|
server_init (const char *wsinstance_sockdir,
|
||||||
const char *cert_session_dir,
|
const char *cert_session_dir,
|
||||||
int idle_timeout,
|
int idle_timeout,
|
||||||
uint16_t port)
|
uint16_t port,
|
||||||
|
unsigned int max_connection_count)
|
||||||
{
|
{
|
||||||
const char *env_listen_fds;
|
const char *env_listen_fds;
|
||||||
struct epoll_event ev = { .events = EPOLLIN };
|
struct epoll_event ev = { .events = EPOLLIN };
|
||||||
|
|
||||||
assert (!server.initialized);
|
assert (!server.initialized);
|
||||||
server.initialized = true;
|
server.initialized = true;
|
||||||
|
server.max_connection_count = max_connection_count;
|
||||||
server.idle_timerfd = -1;
|
server.idle_timerfd = -1;
|
||||||
|
|
||||||
connection_set_directories (wsinstance_sockdir, cert_session_dir);
|
connection_set_directories (wsinstance_sockdir, cert_session_dir);
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,8 @@ void
|
||||||
server_init (const char *wsinstance_sockdir,
|
server_init (const char *wsinstance_sockdir,
|
||||||
const char *cert_session_dir,
|
const char *cert_session_dir,
|
||||||
int idle_timeout,
|
int idle_timeout,
|
||||||
uint16_t port);
|
uint16_t port,
|
||||||
|
unsigned int max_connections);
|
||||||
|
|
||||||
void
|
void
|
||||||
server_run (void);
|
server_run (void);
|
||||||
|
|
|
||||||
|
|
@ -61,8 +61,13 @@ typedef struct {
|
||||||
const char *client_fingerprint;
|
const char *client_fingerprint;
|
||||||
const char *priority;
|
const char *priority;
|
||||||
int expected_pk_algo;
|
int expected_pk_algo;
|
||||||
|
unsigned int max_connection_count;
|
||||||
} TestFixture;
|
} TestFixture;
|
||||||
|
|
||||||
|
static const TestFixture fixture_max_connection_count_10 = {
|
||||||
|
.max_connection_count = 10,
|
||||||
|
};
|
||||||
|
|
||||||
static const TestFixture fixture_separate_crt_key = {
|
static const TestFixture fixture_separate_crt_key = {
|
||||||
.certfile = CERTFILE,
|
.certfile = CERTFILE,
|
||||||
.keyfile = KEYFILE,
|
.keyfile = KEYFILE,
|
||||||
|
|
@ -468,8 +473,10 @@ setup (TestCase *tc, gconstpointer data)
|
||||||
}
|
}
|
||||||
close (socket_dir_fd);
|
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 */
|
/* 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)
|
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
|
static void
|
||||||
test_no_tls_redirect (TestCase *tc, gconstpointer data)
|
test_no_tls_redirect (TestCase *tc, gconstpointer data)
|
||||||
{
|
{
|
||||||
|
|
@ -970,6 +1010,8 @@ main (int argc, char *argv[])
|
||||||
setup, test_no_tls_many_serial, teardown);
|
setup, test_no_tls_many_serial, teardown);
|
||||||
g_test_add ("/server/no-tls/many-parallel", TestCase, NULL,
|
g_test_add ("/server/no-tls/many-parallel", TestCase, NULL,
|
||||||
setup, test_no_tls_many_parallel, teardown);
|
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,
|
g_test_add ("/server/no-tls/redirect", TestCase, NULL,
|
||||||
setup, test_no_tls_redirect, teardown);
|
setup, test_no_tls_redirect, teardown);
|
||||||
g_test_add ("/server/tls/no-client-cert", TestCase, &fixture_separate_crt_key,
|
g_test_add ("/server/tls/no-client-cert", TestCase, &fixture_separate_crt_key,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue