diff --git a/bin/plugin/restricted/selfDelPersonalAccess b/bin/plugin/restricted/selfDelPersonalAccess index 745b365..0456ce0 100755 --- a/bin/plugin/restricted/selfDelPersonalAccess +++ b/bin/plugin/restricted/selfDelPersonalAccess @@ -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); diff --git a/lib/perl/OVH/Bastion/allowkeeper.inc b/lib/perl/OVH/Bastion/allowkeeper.inc index 5c31ea9..31597fb 100644 --- a/lib/perl/OVH/Bastion/allowkeeper.inc +++ b/lib/perl/OVH/Bastion/allowkeeper.inc @@ -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"}; diff --git a/lib/perl/OVH/Bastion/portforwarding.inc b/lib/perl/OVH/Bastion/portforwarding.inc index ffc70d2..2d7fdb5 100644 --- a/lib/perl/OVH/Bastion/portforwarding.inc +++ b/lib/perl/OVH/Bastion/portforwarding.inc @@ -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