feat(portforwarding): enforce account and group limits

pull/597/head
jon4hz 5 months ago
parent 6cd3b2aeed
commit 120fbd0b0d
No known key found for this signature in database
GPG Key ID: 4B0AFE9E7118898E

@ -117,8 +117,8 @@ if ($fnret && $fnret->err ne 'OK_NO_CHANGE' && $remotePort) {
# Now regenerate the sshd config for this account
my @sshd_command = qw{ sudo -n -u root -- /usr/bin/env perl -T };
push @sshd_command, $OVH::Bastion::BASEPATH . '/bin/helper/osh-accountGenerateSshdConfig';
push @sshd_command, '--target', 'self';
push @sshd_command, '--account', $self;
push @sshd_command, '--target', 'self';
push @sshd_command, '--account', $self;
push @sshd_command, '--deleted-local-port', $fnret->value->{'localPort'} if defined $fnret->value->{'localPort'};
my $sshd_fnret = OVH::Bastion::helper(cmd => \@sshd_command);

@ -484,7 +484,13 @@ sub access_modify {
# check port forwarding
if ($remotePort) {
$fnret = OVH::Bastion::is_valid_portforwarding(remotePort => $remotePort);
$fnret = OVH::Bastion::is_valid_portforwarding(
action => $action,
way => $way,
account => $account,
group => $shortGroup,
remotePort => $remotePort
);
$fnret or return $fnret;
$remotePort = $fnret->value->{"remotePort"};

@ -67,17 +67,17 @@ sub _is_port_in_use {
# This function MUST be called while holding the portforwarding lock
# to prevent race conditions between concurrent allocations
sub allocate_local_port {
my $fnret = _get_allocated_local_ports();
$fnret or return $fnret;
my %allocated_ports = keys %{$fnret->value};
# Get the port range configuration
my $fnret = _get_local_port_range();
$fnret = _get_local_port_range();
$fnret or return $fnret;
my $port_range = $fnret->value;
my $min_port = $port_range->{'min'};
my $max_port = $port_range->{'max'};
$fnret = _get_allocated_local_ports();
$fnret or return $fnret;
my %allocated_ports = %{$fnret->value->{'ports'}};
$fnret = _get_unavailable_ports();
$fnret or return $fnret;
my %unavailable_ports = %{$fnret->value};
@ -125,16 +125,64 @@ sub allocate_local_port {
# Validate if a remote port is valid and port count is within allowed limits
sub is_valid_portforwarding {
my %params = @_;
my $remotePort = $params{'remotePort'};
my $action = $params{'action'}; # add or del
my $way = $params{'way'}; # personal, group
my $account = $params{'account'};
my $group = $params{'group'};
my $remotePort = $params{'remotePort'};
my $fnret = OVH::Bastion::is_valid_port(port => $remotePort);
$fnret or osh_exit $fnret;
$remotePort = $fnret->value;
# TODO: check limits based on user/group
if ($action eq 'del') {
return R('OK', value => {remotePort => $remotePort});
}
$fnret = _get_allocated_local_ports();
$fnret or return $fnret;
my $allocatedPorts = $fnret->value;
$fnret = OVH::Bastion::load_configuration();
$fnret or return $fnret;
my $config = $fnret->value;
my $groupMaxForwards = $config->{'portForwardingMaxPerGroup'};
my $accountMaxForwards = $config->{'portForwardingMaxPerUser'};
if ($way eq 'personal') {
my $count = 0;
foreach my $port_info (values %{$allocatedPorts}) {
if ($port_info->{'way'} eq 'personal' && $port_info->{'account'} eq $account) {
$count++;
}
}
if ($count >= $accountMaxForwards) {
return R('ERR_PORTFORWARDING_LIMIT_EXCEEDED',
msg =>
"Account $account has reached the maximum number of allowed port forwardings ($accountMaxForwards)");
}
osh_debug("Account $account has $count/$accountMaxForwards port forwardings");
}
elsif ($way eq 'group') {
my $count = 0;
foreach my $port_info (values %{$allocatedPorts}) {
if ($port_info->{'way'} eq 'group' && $port_info->{'group'} eq $group) {
$count++;
}
}
if ($count >= $groupMaxForwards) {
return R('ERR_PORTFORWARDING_LIMIT_EXCEEDED',
msg => "Group $group has reached the maximum number of allowed port forwardings ($groupMaxForwards)");
}
osh_debug("Group $group has $count/$groupMaxForwards port forwardings");
}
elsif ($way eq 'groupguest') {
return R('OK', value => {remotePort => $remotePort});
}
else {
return R('ERR_INVALID_PARAMETER', msg => "Invalid way parameter: $way");
}
return R('OK', value => {remotePort => $remotePort});
}
@ -156,7 +204,7 @@ sub _get_allocated_local_ports {
next if not $grantedGroup;
foreach my $entry (@{$grantedGroup->value || []}) {
if (defined $entry->{'localPort'}) {
$allocated_local_ports{$entry->{'localPort'}} = 1;
$allocated_local_ports{$entry->{'localPort'}} = {way => 'group', group => $group};
}
}
}
@ -166,13 +214,13 @@ sub _get_allocated_local_ports {
next if not $grantedPersonal;
foreach my $entry (@{$grantedPersonal->value || []}) {
if (defined $entry->{'localPort'}) {
$allocated_local_ports{$entry->{'localPort'}} = 1;
$allocated_local_ports{$entry->{'localPort'}} = {way => 'personal', account => $account};
}
}
}
osh_debug("Allocated local ports: " . join(", ", keys %allocated_local_ports));
return R('OK', value => {ports => \%allocated_local_ports});
return R('OK', value => \%allocated_local_ports);
}
# Get the configured port range free for port allocations used to allocate local ports

Loading…
Cancel
Save