Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 28 additions & 3 deletions bin/helper/osh-groupModify
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use OVH::Result;
# Fetch command options
my $fnret;
my ($result, @optwarns);
my ($group, $mfaRequired, $ttl, $idleLockTimeout, $idleKillTimeout);
my ($group, $mfaRequired, $ttl, $idleLockTimeout, $idleKillTimeout, $tryPersonalKeys);
eval {
local $SIG{__WARN__} = sub { push @optwarns, shift };
$result = GetOptions(
Expand All @@ -27,6 +27,7 @@ eval {
"guest-ttl-limit=i" => \$ttl,
"idle-lock-timeout=i" => \$idleLockTimeout,
"idle-kill-timeout=i" => \$idleKillTimeout,
"try-personal-keys=s" => \$tryPersonalKeys,
);
};
if ($@) { die $@ }
Expand All @@ -42,9 +43,11 @@ if (!$group) {
HEXIT('ERR_MISSING_PARAMETER', msg => "Missing argument 'group'");
}

if (!$mfaRequired && !defined $ttl && !defined $idleLockTimeout && !defined $idleKillTimeout) {
if (!$mfaRequired && !defined $ttl && !defined $idleLockTimeout && !defined $idleKillTimeout && !$tryPersonalKeys) {
HEXIT('ERR_MISSING_PARAMETER',
msg => "Missing argument 'mfa-required', 'guest-ttl-limit', 'idle-lock-timeout' or 'idle-kill-timeout'");
msg =>
"Missing argument 'mfa-required', 'guest-ttl-limit', 'idle-lock-timeout', 'idle-kill-timeout' or 'try-personal-keys'"
);
}

#<HEADER
Expand Down Expand Up @@ -91,6 +94,28 @@ if (defined $mfaRequired) {
}
}

if (defined $tryPersonalKeys) {
osh_info "Modifying try-personal-keys policy of group...";
if (grep { $tryPersonalKeys eq $_ } qw{ yes no }) {
$fnret = OVH::Bastion::group_config(
group => $group,
%{OVH::Bastion::OPT_GROUP_TRY_PERSONAL_KEYS()},
value => ($tryPersonalKeys eq 'yes' ? 1 : 0)
);
if ($fnret) {
osh_info "... done, policy is now: $tryPersonalKeys";
}
else {
osh_warn "... error while changing try-personal-keys policy (" . $fnret->msg . ")";
}
$result{'try_personal_keys'} = $fnret;
}
else {
osh_warn "... invalid option '$tryPersonalKeys'";
$result{'try_personal_keys'} = R('ERR_INVALID_PARAMETER');
}
}

my %idleTimeout = (
lock => {
name => "idle lock timeout",
Expand Down
18 changes: 16 additions & 2 deletions bin/plugin/group-owner/groupModify
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,12 @@ my $remainingOptions = OVH::Bastion::Plugin::begin(
"idle-lock-timeout=s" => \my $idleLockTimeout,
"idle-kill-timeout=s" => \my $idleKillTimeout,
"guest-ttl-limit=s" => \my $ttl,
"try-personal-keys=s" => \my $tryPersonalKeys,
},
helptext => <<'EOF',
Modify the configuration of a group

Usage: --osh SCRIPT_NAME --group GROUP [--mfa-required password|totp|any|none] [--guest-ttl-limit DURATION]
Usage: --osh SCRIPT_NAME --group GROUP [--mfa-required password|totp|any|none] [--guest-ttl-limit DURATION] [--try-personal-keys yes|no]

--group GROUP Name of the group to modify
--mfa-required password|totp|any|none Enforce UNIX password requirement, or TOTP requirement, or any MFA requirement, when connecting to a server of the group
Expand All @@ -31,6 +32,8 @@ Usage: --osh SCRIPT_NAME --group GROUP [--mfa-required password|totp|any|none] [
this group. If set to -1, remove this group override and use the global setting instead.
--guest-ttl-limit DURATION This group will enforce TTL setting, on guest access creation, to be set, and not to a higher value than DURATION,
set to zero to allow guest accesses creation without any TTL set (default)
--try-personal-keys yes|no When a user accesses a server through his group permission, his personal access keys will also be added
to the connection attempt (default: no)

Note that `--idle-lock-timeout` and `--idle-kill-timeout` will NOT be applied for catch-all groups (having 0.0.0.0/0 in their server list).

Expand All @@ -48,7 +51,12 @@ if (!$group) {
help();
osh_exit 'ERR_MISSING_PARAMETER', "Missing mandatory parameter 'group'";
}
if (!$mfaRequired && !defined $ttl && !defined $idleLockTimeout && !defined $idleKillTimeout) {
if ( !$mfaRequired
&& !defined $ttl
&& !defined $idleLockTimeout
&& !defined $idleKillTimeout
&& !defined $tryPersonalKeys)
{
help();
osh_exit 'ERR_MISSING_PARAMETER', "Nothing to modify";
}
Expand Down Expand Up @@ -81,6 +89,11 @@ if (defined $mfaRequired && !grep { $mfaRequired eq $_ } qw{ password totp any n
osh_exit 'ERR_INVALID_PARAMETER', "Expected 'password', 'totp', 'any' or 'none' as parameter to --mfa-required";
}

if (defined $tryPersonalKeys && !grep { $tryPersonalKeys eq $_ } qw{ yes no }) {
help();
osh_exit 'ERR_INVALID_PARAMETER', "Expected 'yes' or 'no' as parameter to --try-personal-keys";
}

my @command = qw{ sudo -n -u };
push @command, $group;
push @command, qw{ -- /usr/bin/env perl -T };
Expand All @@ -90,5 +103,6 @@ push @command, '--mfa-required', $mfaRequired if $mfaRequired;
push @command, '--guest-ttl-limit', $ttl if defined $ttl;
push @command, '--idle-lock-timeout', $idleLockTimeout if defined $idleLockTimeout;
push @command, '--idle-kill-timeout', $idleKillTimeout if defined $idleKillTimeout;
push @command, '--try-personal-keys', $tryPersonalKeys if defined $tryPersonalKeys;

osh_exit OVH::Bastion::helper(cmd => \@command);
16 changes: 16 additions & 0 deletions bin/plugin/open/groupInfo
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,11 @@ foreach my $groupData (@groups) {
if ($fnret && defined $fnret->value && $fnret->value =~ /^-?\d+$/) {
$ret{'idle_lock_timeout'} = $fnret->value;
}

$fnret = OVH::Bastion::group_config(group => $group, %{OVH::Bastion::OPT_GROUP_TRY_PERSONAL_KEYS()});
if ($fnret && defined $fnret->value) {
$ret{'try_personal_keys'} = ($fnret->value eq '1' ? 'yes' : 'no');
}
}

# group egress keys if we've been asked those
Expand Down Expand Up @@ -302,6 +307,17 @@ sub print_group_info {
osh_warn "Specific idle kill timeout: idle sessions on servers of this group will $action";
}

if ($ret{'try_personal_keys'}) {
osh_info ' ';
if ($ret{'try_personal_keys'} eq 'yes') {
osh_info
"Personal keys: When group members access servers from this group, their personal egress keys will also be tried.";
}
else {
osh_info "Personal keys: No personal egress keys will be used.";
}
}

if ($withKeys) {
osh_info ' ';
if (!%{$ret{'keys'}}) {
Expand Down
3 changes: 2 additions & 1 deletion bin/shell/connect.pl
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,8 @@ sub exit_sig {
1) Check the remote account's authorized_keys on $ip, did you add the proper key there? (personal key or group key)
2) Did you tell the bastion you added a key to the remote server, so it knows it has to use it? See the actually used keys just above. If you didn't, do it with selfAddPersonalAccess or groupAddServer.
3) Check the from="" part of the remote account's authorized_keys' keyline. Are all the bastion IPs present? Master and slave(s)? See groupInfo or selfListEgressKeys to get the proper keyline to copy/paste.
4) Did you check the 3 above points carefully? Really? Because if you did, you wouldn't be reading this 4th bullet point, as your problem would already be fixed ;)
4) Are you trying to access a remote server through a group permission but the remote server expects your personal access key? Ask your group owner to enable the 'try-personal-keys' policy.
5) Did you check the 4 above points carefully? Really? Because if you did, you wouldn't be reading this 5th bullet point, as your problem would already be fixed ;)
EOS
);
}
Expand Down
1 change: 1 addition & 0 deletions lib/perl/OVH/Bastion.pm
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ use constant {

OPT_GROUP_IDLE_LOCK_TIMEOUT => {key => 'idle_lock_timeout'},
OPT_GROUP_IDLE_KILL_TIMEOUT => {key => 'idle_kill_timeout'},
OPT_GROUP_TRY_PERSONAL_KEYS => {key => 'try_personal_keys'},
};

###########
Expand Down
36 changes: 36 additions & 0 deletions lib/perl/OVH/Bastion/allowdeny.inc
Original file line number Diff line number Diff line change
Expand Up @@ -859,6 +859,16 @@ sub is_access_granted {
# normal member case, just reuse $grantedGroup
osh_debug("is_access_granted: adding grantedGroup to grants because is member");
push @grants, {type => 'group-member', group => $shortGroup, %{$grantedGroup->value}};

# check if group has the "try-personal-keys" option enabled, and if so, add personal keys too
my $tryPersonalKeysConfig =
OVH::Bastion::group_config(group => $group, %{OVH::Bastion::OPT_GROUP_TRY_PERSONAL_KEYS()});
if ($tryPersonalKeysConfig && $tryPersonalKeysConfig->value && $tryPersonalKeysConfig->value eq '1') {
osh_debug(
"is_access_granted: try-personal-keys is enabled for group $shortGroup, adding personal access");
push @grants, {type => 'personal-via-group', group => $shortGroup, %{$grantedGroup->value}};
}

}
elsif (OVH::Bastion::is_group_guest(group => $shortGroup, account => $account, sudo => $params{'sudo'})) {

Expand All @@ -871,6 +881,16 @@ sub is_access_granted {
if ($grantedGuest && $grantedGroup) {
push @grants, {type => 'group-guest', group => $shortGroup, %{$grantedGuest->value}};
osh_debug("is_access_granted: adding grantedGuest to grants because is guest and group has access");

# check if group has the "try-personal-keys" option enabled, and if so, add personal keys too
my $tryPersonalKeysConfig =
OVH::Bastion::group_config(group => $group, %{OVH::Bastion::OPT_GROUP_TRY_PERSONAL_KEYS()});
if ($tryPersonalKeysConfig && $tryPersonalKeysConfig->value && $tryPersonalKeysConfig->value eq '1') {
osh_debug(
"is_access_granted: try-personal-keys is enabled for group $shortGroup, adding personal access"
);
push @grants, {type => 'personal-via-group', group => $shortGroup, %{$grantedGroup->value}};
}
}

# special legacy case; we also check if account has a legacy access for ip AND that the group ALSO has access to this ip
Expand Down Expand Up @@ -913,6 +933,22 @@ sub is_access_granted {
$data{'mfa'} =
OVH::Bastion::account_config(key => "personal_egress_mfa_required", account => $sysaccount);
}
elsif ($access->{'type'} eq 'personal-via-group') {
# This is personal access triggered by try-personal-keys group option
# Use personal keys but group MFA and timeout settings
$data{'keys'} = OVH::Bastion::get_personal_account_keys(
account => $sysaccount,
listOnly => $listOnly,
noexec => $noexec,
forceKey => $access->{'forceKey'}
);
# Use group MFA and timeout settings since access is via group
$data{'mfa'} = OVH::Bastion::group_config(key => "mfa_required", group => $access->{'group'});
$data{'idle_lock_timeout'} = OVH::Bastion::group_config(%{OVH::Bastion::OPT_GROUP_IDLE_LOCK_TIMEOUT()},
group => $access->{'group'});
$data{'idle_kill_timeout'} = OVH::Bastion::group_config(%{OVH::Bastion::OPT_GROUP_IDLE_KILL_TIMEOUT()},
group => $access->{'group'});
}
else {
# unknown access type? no key!
warn_syslog("Unknown access type '" . $access->{'type'} . "' found, ignoring");
Expand Down
82 changes: 82 additions & 0 deletions tests/functional/tests.d/350-groups.sh
Original file line number Diff line number Diff line change
Expand Up @@ -1088,6 +1088,34 @@ EOS
success guest_ttl_limit $a1 --osh groupModify --group $group1 --guest-ttl-limit 0
json .command groupModify .error_code OK

# try-personal-keys tests
# test invalid parameter values for try-personal-keys
plgfail invalid_try_personal_keys_value $a1 --osh groupModify --group $group1 --try-personal-keys invalid
json .error_code ERR_INVALID_PARAMETER

# enable try-personal-keys
success enable_try_personal_keys $a1 --osh groupModify --group $group1 --try-personal-keys yes
json .error_code OK .command groupModify .value.try_personal_keys.error_code OK

# verify the setting is applied in groupInfo
success check_try_personal_keys_enabled $a1 --osh groupInfo --group $group1
contain "personal egress keys will also be tried"
json .error_code OK .command groupInfo .value.try_personal_keys yes

# disable try-personal-keys
success disable_try_personal_keys $a1 --osh groupModify --group $group1 --try-personal-keys no
json .error_code OK .command groupModify .value.try_personal_keys.error_code OK

# verify the setting is applied in groupInfo
success check_try_personal_keys_disabled $a1 --osh groupInfo --group $group1
contain "No personal egress keys will be used"
json .error_code OK .command groupInfo .value.try_personal_keys no

# non-owner cannot modify try-personal-keys
run non_owner_modify_try_personal_keys $a2 --osh groupModify --group $group1 --try-personal-keys yes
retvalshouldbe 106
json .error_code KO_RESTRICTED_COMMAND

# if we're just counting the number of tests, don't sleep
[ "$COUNTONLY" != 1 ] && sleep 1

Expand Down Expand Up @@ -1147,6 +1175,60 @@ EOS
nocontain "$group1"
contain "personal access"

# try-personal-keys tests
# First, make account2 a member instead of just a guest for proper testing
success add_a2_as_member_for_try_personal_keys $a1 --osh groupAddMember --group $group1 --account $account2
json .error_code OK .command groupAddMember

# Without try-personal-keys enabled - should only show group access
run a2_group_access_only $a2 127.0.0.11 --user testuser
retvalshouldbe 255
contain "allowed ... log on"
contain "group-member of $group1"
nocontain "personal-via-group"

# Enable try-personal-keys and test access
success enable_try_personal_keys_for_access $a1 --osh groupModify --group $group1 --try-personal-keys yes
json .error_code OK .command groupModify

# Now connection should show both group and personal access
run a2_group_and_personal_access $a2 127.0.0.11 --user testuser
retvalshouldbe 255
contain "allowed ... log on"
contain "group-member of $group1"
contain "personal-via-group"

# Disable try-personal-keys again
success disable_try_personal_keys_for_access $a1 --osh groupModify --group $group1 --try-personal-keys no
json .error_code OK .command groupModify

# Should only show group access again
run a2_group_access_only_again $a2 127.0.0.11 --user testuser
retvalshouldbe 255
contain "allowed ... log on"
contain "group-member of $group1"
nocontain "personal-via-group"

# Enable try-personal-keys again
success enable_try_personal_keys_again $a1 --osh groupModify --group $group1 --try-personal-keys yes
json .error_code OK .command groupModify

# Remove account2 from group members (this will also remove guest access)
success remove_a2_from_members $a1 --osh groupDelMember --group $group1 --account $account2
json .error_code OK .command groupDelMember

# Add account2 back as guest to test guest access only
success add_a2_as_guest_after_removal $a1 --osh groupAddGuestAccess --group $group1 --account $account2 --host 127.0.0.10 --user testuser --port 22
json .error_code OK .command groupAddGuestAccess

# Account2 is now guest but can still use personal access keys to connect
run a2_guest_access_only_after_member_removal $a2 127.0.0.10 --user testuser
retvalshouldbe 255
contain "allowed ... log on"
contain "group-guest of $group1"
nocontain "group-member"
contain "personal-via-group"

# group1: a1(owner,aclkeeper,gatekeeper,member) a2(guest(127.0.0.10)) servers(127.0.0.10,127.0.0.11)
# account1: perso([email protected]:22)
plgfail transmitownership_noright1 $a3 --osh groupTransmitOwnership --group $group1 --account $account1
Expand Down