Skip to content

Commit

Permalink
YDA-5789: limit max number of client connections (#468)
Browse files Browse the repository at this point in the history
Co-authored-by: claravox <[email protected]>
Co-authored-by: Sietse Snel <[email protected]>
  • Loading branch information
3 people authored Jul 16, 2024
1 parent dc2dfca commit cc0a4ad
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 1 deletion.
19 changes: 19 additions & 0 deletions policies.py
Original file line number Diff line number Diff line change
Expand Up @@ -617,6 +617,25 @@ def rule_check_anonymous_access_allowed(ctx, address):
return "true" if address in permit_list else "false"


@rule.make(inputs=[], outputs=[0])
def rule_check_max_connections_exceeded(ctx):
"""Check if user exceeds the maximum number of connections.
:param ctx: Combined type of a callback and rei struct
:returns: 'true' if maximum number of connections is exceeded; otherwise 'false'.
Also returns 'false' if the maximum number of connections check has been
disabled, or if the maximum number does not apply to the present user.
"""
if not config.user_max_connections_enabled:
return "false"
elif user.name(ctx) in ['anonymous', 'rods']:
return "false"
else:
connections = user.number_of_connections(ctx)
return "false" if connections <= config.user_max_connections_number else "true"


@rule.make(inputs=[0, 1, 2, 3, 4], outputs=[])
def pep_database_gen_query_pre(ctx, dbtype, _ctx, results, genquery_inp, genquery_out):
if not is_safe_genquery_inp(genquery_inp):
Expand Down
2 changes: 2 additions & 0 deletions util/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,8 @@ def __repr__(self):
vault_copy_backoff_time=300,
vault_copy_max_retries=5,
vault_copy_multithread_enabled=True,
user_max_connections_enabled=False,
user_max_connections_number=4,
python3_interpreter='/usr/local/bin/python3')

# }}}
Expand Down
21 changes: 20 additions & 1 deletion util/user.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
# -*- coding: utf-8 -*-
"""Utility / convenience functions for querying user info."""

__copyright__ = 'Copyright (c) 2019-2022, Utrecht University'
__copyright__ = 'Copyright (c) 2019-2024, Utrecht University'
__license__ = 'GPLv3, see LICENSE'

import subprocess
from collections import namedtuple

import genquery
import session_vars

import log

# User is a tuple consisting of a name and a zone, which stringifies into 'user#zone'.
User = namedtuple('User', ['name', 'zone'])
User.__str__ = lambda self: '{}#{}'.format(*self)
Expand Down Expand Up @@ -104,3 +107,19 @@ def name_from_id(ctx, user_id):
genquery.AS_LIST, ctx):
return row[0]
return ''


def number_of_connections(ctx):
"""Get number of active connections from client user."""
connections = 0
try:
# We don't use the -a option with the ips command, because this takes
# significantly more time, which would significantly reduce performance.
ips = subprocess.check_output(["ips"])
username = session_vars.get_map(ctx.rei)['client_user']['user_name']
connections = ips.count(username)
except Exception as e:
log.write(ctx, "Error: unable to determine number of user connections: " + str(e))
return 0

return connections
7 changes: 7 additions & 0 deletions uuPolicies.r
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,13 @@ pep_api_auth_request_pre(*instanceName, *comm, *request) {
}
}

*max_connections_exceeded = '';
rule_check_max_connections_exceeded(*max_connections_exceeded);
if ( *max_connections_exceeded == "true" ) {
writeLine("serverLog", "Refused access for *user_name#*zone_name, max connections exceeded.");
failmsg(-1, "Refused access for *user_name#*zone_name, max connections exceeded.");
}

writeLine("serverLog", "{*user_name#*zone_name} Agent process started from *client_addr");
}

Expand Down

0 comments on commit cc0a4ad

Please sign in to comment.