feat: groupModify: add --idle-lock-timeout and --idle-kill-timeout for group-specific timeouts

This commit is contained in:
Stéphane Lesimple 2022-04-05 12:25:17 +00:00 committed by Stéphane Lesimple
parent 6fb528ccf1
commit 46a01a546a
8 changed files with 345 additions and 45 deletions

View file

@ -41,7 +41,8 @@ if echo "$DISTRO_LIKE" | grep -q -w debian; then
if [ "$(uname -m)" = armv7l ]; then if [ "$(uname -m)" = armv7l ]; then
wanted_list="$wanted_list wget" wanted_list="$wanted_list wget"
fi fi
[ "$opt_dev" = 1 ] && wanted_list="$wanted_list libperl-critic-perl perltidy shellcheck openssl" [ "$opt_dev" = 1 ] && wanted_list="$wanted_list libperl-critic-perl libtest-deep-perl perltidy shellcheck openssl wget"
if { [ "$LINUX_DISTRO" = debian ] && [ "$DISTRO_VERSION_MAJOR" -lt 9 ]; } || if { [ "$LINUX_DISTRO" = debian ] && [ "$DISTRO_VERSION_MAJOR" -lt 9 ]; } ||
{ [ "$LINUX_DISTRO" = ubuntu ] && [ "$DISTRO_VERSION_MAJOR" -le 16 ]; }; then { [ "$LINUX_DISTRO" = ubuntu ] && [ "$DISTRO_VERSION_MAJOR" -le 16 ]; }; then
wanted_list="$wanted_list openssh-blacklist openssh-blacklist-extra" wanted_list="$wanted_list openssh-blacklist openssh-blacklist-extra"
@ -70,7 +71,7 @@ elif echo "$DISTRO_LIKE" | grep -q -w rhel; then
expect openssh-server netcat bash perl-CGI perl-Test-Simple passwd \ expect openssh-server netcat bash perl-CGI perl-Test-Simple passwd \
cracklib-dicts perl-Time-Piece perl-Time-HiRes diffutils \ cracklib-dicts perl-Time-Piece perl-Time-HiRes diffutils \
perl-Sys-Syslog pamtester google-authenticator qrencode-libs \ perl-Sys-Syslog pamtester google-authenticator qrencode-libs \
perl-LWP-Protocol-https findutils tar" perl-LWP-Protocol-https perl-Test-Deep findutils tar"
if [ "$DISTRO_VERSION_MAJOR" = 7 ]; then if [ "$DISTRO_VERSION_MAJOR" = 7 ]; then
wanted_list="$wanted_list fortune-mod coreutils util-linux" wanted_list="$wanted_list fortune-mod coreutils util-linux"
else else
@ -78,6 +79,7 @@ elif echo "$DISTRO_LIKE" | grep -q -w rhel; then
fi fi
[ "$opt_syslogng" = 1 ] && wanted_list="$wanted_list syslog-ng" [ "$opt_syslogng" = 1 ] && wanted_list="$wanted_list syslog-ng"
if [ "$opt_install" = 1 ]; then if [ "$opt_install" = 1 ]; then
if [ "$DISTRO_VERSION_MAJOR" = 8 ]; then if [ "$DISTRO_VERSION_MAJOR" = 8 ]; then
# in December 2020, they added "-Linux" to their repo name, so trying both combinations # in December 2020, they added "-Linux" to their repo name, so trying both combinations
@ -115,7 +117,7 @@ elif echo "$DISTRO_LIKE" | grep -q -w suse; then
perl-Net-Server cryptsetup mosh expect openssh \ perl-Net-Server cryptsetup mosh expect openssh \
coreutils netcat-openbsd bash perl-CGI iputils \ coreutils netcat-openbsd bash perl-CGI iputils \
perl-Time-HiRes perl-Unix-Syslog hostname perl-LWP-Protocol-https \ perl-Time-HiRes perl-Unix-Syslog hostname perl-LWP-Protocol-https \
google-authenticator-libpam tar" google-authenticator-libpam tar perl-Test-Deep"
[ "$opt_syslogng" = 1 ] && wanted_list="$wanted_list syslog-ng" [ "$opt_syslogng" = 1 ] && wanted_list="$wanted_list syslog-ng"
if [ "$opt_install" = 1 ]; then if [ "$opt_install" = 1 ]; then
@ -129,7 +131,8 @@ elif echo "$DISTRO_LIKE" | grep -q -w suse; then
elif [ "$OS_FAMILY" = FreeBSD ]; then elif [ "$OS_FAMILY" = FreeBSD ]; then
wanted_list="base64 coreutils rsync bash sudo pamtester p5-JSON p5-JSON-XS gnupg \ wanted_list="base64 coreutils rsync bash sudo pamtester p5-JSON p5-JSON-XS gnupg \
p5-common-sense p5-DateTime p5-Net-IP p5-DBD-SQLite p5-Net-Netmask lsof \ p5-common-sense p5-DateTime p5-Net-IP p5-DBD-SQLite p5-Net-Netmask lsof \
p5-Term-ReadKey expect fping p5-Net-Server p5-CGI p5-LWP-Protocol-https" p5-Term-ReadKey expect fping p5-Net-Server p5-CGI p5-LWP-Protocol-https \
p5-Test-Deep"
install_cmd="pkg add" install_cmd="pkg add"
installed="" installed=""
for i in $wanted_list for i in $wanted_list

View file

@ -18,13 +18,15 @@ use OVH::Result;
# Fetch command options # Fetch command options
my $fnret; my $fnret;
my ($result, @optwarns); my ($result, @optwarns);
my ($group, $mfaRequired, $ttl); my ($group, $mfaRequired, $ttl, $idleLockTimeout, $idleKillTimeout);
eval { eval {
local $SIG{__WARN__} = sub { push @optwarns, shift }; local $SIG{__WARN__} = sub { push @optwarns, shift };
$result = GetOptions( $result = GetOptions(
"group=s" => sub { $group //= $_[1] }, "group=s" => sub { $group //= $_[1] },
"mfa-required=s" => \$mfaRequired, "mfa-required=s" => \$mfaRequired,
"guest-ttl-limit=i" => \$ttl, "guest-ttl-limit=i" => \$ttl,
"idle-lock-timeout=i" => \$idleLockTimeout,
"idle-kill-timeout=i" => \$idleKillTimeout,
); );
}; };
if ($@) { die $@ } if ($@) { die $@ }
@ -40,8 +42,9 @@ if (!$group) {
HEXIT('ERR_MISSING_PARAMETER', msg => "Missing argument 'group'"); HEXIT('ERR_MISSING_PARAMETER', msg => "Missing argument 'group'");
} }
if (!$mfaRequired && !defined $ttl) { if (!$mfaRequired && !defined $ttl && !defined $idleLockTimeout && !defined $idleKillTimeout) {
HEXIT('ERR_MISSING_PARAMETER', msg => "Missing argument 'mfa-required' or 'guest-ttl-limit'"); HEXIT('ERR_MISSING_PARAMETER',
msg => "Missing argument 'mfa-required', 'guest-ttl-limit', 'idle-lock-timeout' or 'idle-kill-timeout'");
} }
#<HEADER #<HEADER
@ -88,6 +91,69 @@ if (defined $mfaRequired) {
} }
} }
my %idleTimeout = (
lock => {
name => "idle lock timeout",
key => \%{OVH::Bastion::OPT_GROUP_IDLE_LOCK_TIMEOUT()},
value => $idleLockTimeout,
},
kill => {
name => "idle kill timeout",
key => \%{OVH::Bastion::OPT_GROUP_IDLE_KILL_TIMEOUT()},
value => $idleKillTimeout,
},
);
foreach my $item (keys %idleTimeout) {
next if !defined $idleTimeout{$item}{'value'};
osh_info "Modifying " . $idleTimeout{$item}{'name'} . " policy of group...";
if ($idleTimeout{$item}{'value'} >= 0) {
$fnret = OVH::Bastion::group_config(
group => $group,
%{$idleTimeout{$item}{'key'}}, value => $idleTimeout{$item}{'value'}
);
if ($fnret) {
if ($idleTimeout{$item}{'value'} == 0) {
osh_info "... done, this group's " . $idleTimeout{$item}{'name'} . " policy is now set to: disabled";
}
else {
osh_info "... done, this group is now configured to use a "
. $idleTimeout{$item}{'name'}
. " policy of "
. OVH::Bastion::duration2human(seconds => $idleTimeout{$item}{'value'})->value->{'human'};
}
}
else {
osh_warn "... error while setting the group-specific "
. $idleTimeout{$item}{'name'}
. " policy ("
. $fnret->msg . ")";
warn_syslog "Error setting the group-specific "
. $idleTimeout{$item}{'name'}
. " policy of $group ("
. $fnret->msg . ")";
}
}
else {
$fnret = OVH::Bastion::group_config(group => $group, %{$idleTimeout{$item}{'key'}}, delete => 1);
if ($fnret) {
osh_info "... done, this group will now use the global " . $idleTimeout{$item}{'name'} . " policy";
}
else {
osh_warn "... error while removing the group-specific "
. $idleTimeout{$item}{'name'}
. " policy ("
. $fnret->msg . ")";
warn_syslog "Error removing the group-specific "
. $idleTimeout{$item}{'name'}
. " policy of $group ("
. $fnret->msg . ")";
}
}
$result{$idleTimeout{$item}{'key'}} = $fnret;
}
if (defined $ttl) { if (defined $ttl) {
osh_info "Modifying guest TTL limit policy of group..."; osh_info "Modifying guest TTL limit policy of group...";
if ($ttl > 0) { if ($ttl > 0) {

View file

@ -14,6 +14,8 @@ my $remainingOptions = OVH::Bastion::Plugin::begin(
options => { options => {
"group=s" => \my $group, "group=s" => \my $group,
"mfa-required=s" => \my $mfaRequired, "mfa-required=s" => \my $mfaRequired,
"idle-lock-timeout=s" => \my $idleLockTimeout,
"idle-kill-timeout=s" => \my $idleKillTimeout,
"guest-ttl-limit=s" => \my $ttl, "guest-ttl-limit=s" => \my $ttl,
}, },
helptext => <<'EOF', helptext => <<'EOF',
@ -23,8 +25,20 @@ Usage: --osh SCRIPT_NAME --group GROUP [--mfa-required password|totp|any|none] [
--group GROUP Name of the group to modify --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 --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
--idle-lock-timeout DURATION|0|-1 Overrides the global setting (`idleLockTimeout`), to the specified duration. If set to 0, disables `idleLockTimeout` for
this group. If set to -1, remove this group override and use the global setting instead.
--idle-kill-timeout DURATION|0|-1 Overrides the global setting (`idleKillTimeout`), to the specified duration. If set to 0, disables `idleKillTimeout` for
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, --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) set to zero to allow guest accesses creation without any TTL set (default)
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).
If a server is in exactly one group an account is a member of, then its values of `--idle-lock-timeout` and `--idle-kill-timeout`, if set,
will prevail over the global setting. The global setting can be seen with `--osh info`.
Otherwise, the most restrictive setting (i.e. the one with the lower strictly positive duration) between
all the considered groups and the global setting, will be used.
EOF EOF
); );
@ -34,14 +48,22 @@ if (!$group) {
help(); help();
osh_exit 'ERR_MISSING_PARAMETER', "Missing mandatory parameter 'group'"; osh_exit 'ERR_MISSING_PARAMETER', "Missing mandatory parameter 'group'";
} }
if (!$mfaRequired && !defined $ttl) { if (!$mfaRequired && !defined $ttl && !defined $idleLockTimeout && !defined $idleKillTimeout) {
help(); help();
osh_exit 'ERR_MISSING_PARAMETER', "Nothing to modify"; osh_exit 'ERR_MISSING_PARAMETER', "Nothing to modify";
} }
if (defined $ttl) { foreach my $item (\$ttl, \$idleLockTimeout, \$idleKillTimeout) {
$fnret = OVH::Bastion::is_valid_ttl(ttl => $ttl); if (defined $$item && $$item != -1) {
$fnret = OVH::Bastion::is_valid_ttl(ttl => $$item);
$fnret or osh_exit $fnret; $fnret or osh_exit $fnret;
$ttl = $fnret->value->{'seconds'}; $$item = $fnret->value->{'seconds'};
}
}
# ttl doesn't allow -1 as a valid value, check that
if ($ttl == -1) {
osh_exit 'ERR_INVALID_PARAMETER',
"Invalid TTL (-1), expected an amount of seconds, or a duration string such as '2d8h15m'";
} }
$fnret = OVH::Bastion::is_valid_group_and_existing(group => $group, groupType => 'key'); $fnret = OVH::Bastion::is_valid_group_and_existing(group => $group, groupType => 'key');
@ -66,5 +88,7 @@ push @command, $OVH::Bastion::BASEPATH . '/bin/helper/osh-groupModify';
push @command, '--group', $group; push @command, '--group', $group;
push @command, '--mfa-required', $mfaRequired if $mfaRequired; push @command, '--mfa-required', $mfaRequired if $mfaRequired;
push @command, '--guest-ttl-limit', $ttl if defined $ttl; 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;
osh_exit OVH::Bastion::helper(cmd => \@command); osh_exit OVH::Bastion::helper(cmd => \@command);

View file

@ -176,11 +176,25 @@ if ( OVH::Bastion::is_group_owner(group => $shortGroup, account => $self, supe
} }
$fnret = OVH::Bastion::group_config(group => $group, key => 'guest_ttl_limit'); $fnret = OVH::Bastion::group_config(group => $group, key => 'guest_ttl_limit');
if ($fnret) { if ($fnret && defined $fnret->value && $fnret->value =~ /^\d+$/) {
osh_warn "Guest TTL enforced: guest accesses must have a TTL with a maximum duration of " osh_warn "Guest TTL enforced: guest accesses must have a TTL with a maximum duration of "
. OVH::Bastion::duration2human(seconds => $fnret->value)->value->{'duration'}; . OVH::Bastion::duration2human(seconds => $fnret->value)->value->{'duration'};
$result_hash->{'guest_ttl_limit'} = $fnret->value; $result_hash->{'guest_ttl_limit'} = $fnret->value;
} }
$fnret = OVH::Bastion::group_config(group => $group, %{OVH::Bastion::OPT_GROUP_IDLE_KILL_TIMEOUT()});
if ($fnret && defined $fnret->value && $fnret->value =~ /^-?\d+$/) {
osh_warn "Specific idle kill timeout: idle sessions on servers of this group will be cut after "
. OVH::Bastion::duration2human(seconds => $fnret->value)->value->{'duration'};
$result_hash->{'idle_kill_timeout'} = $fnret->value;
}
$fnret = OVH::Bastion::group_config(group => $group, => %{OVH::Bastion::OPT_GROUP_IDLE_LOCK_TIMEOUT()});
if ($fnret && defined $fnret->value && $fnret->value =~ /^-?\d+$/) {
osh_warn "Specific idle lock timeout: idle sessions on servers of this group will be locked after "
. OVH::Bastion::duration2human(seconds => $fnret->value)->value->{'duration'};
$result_hash->{'idle_lock_timeout'} = $fnret->value;
}
} }
else { else {
osh_info "You should ask him/her/them if you think you need access for your work tasks."; osh_info "You should ask him/her/them if you think you need access for your work tasks.";

View file

@ -1172,7 +1172,7 @@ if ($osh_debug) {
} }
# build ttyrec command that'll prefix the real command # build ttyrec command that'll prefix the real command
my $ttyrec_fnret = OVH::Bastion::build_ttyrec_cmdline( my $ttyrec_fnret = OVH::Bastion::build_ttyrec_cmdline_part1of2(
ip => $ip, ip => $ip,
port => $port, port => $port,
user => $user, user => $user,
@ -1310,6 +1310,60 @@ else {
push @command, '/usr/bin/ssh', $ip, '-l', $user, '-p', $port; push @command, '/usr/bin/ssh', $ip, '-l', $user, '-p', $port;
# before listing the accesses and the keys they use, first compute the correct
# idle-kill-timeout and idle-lock-timeout value, as these can be overriden for group accesses,
# see the help of groupModify command for details on the algorithm's logic, it is also commented below
# First, we init the vars with the global setting.
my %idleTimeout = (
kill => OVH::Bastion::config("idleKillTimeout")->value,
lock => OVH::Bastion::config("idleLockTimeout")->value,
);
# Then, gather all the timeouts overrides that may be defined for the matching groups
my %idleTimeoutsOverride = (kill => [], lock => []);
foreach my $access (@accessList) {
next if ($access->{'type'} !~ /^group/);
push @{$idleTimeoutsOverride{'kill'}}, $access->{'idleKillTimeout'}
if (defined $access->{'idleKillTimeout'} && $access->{'size'} != 2**32);
push @{$idleTimeoutsOverride{'lock'}}, $access->{'idleLockTimeout'}
if (defined $access->{'idleLockTimeout'} && $access->{'size'} != 2**32);
}
# Now, decide what to apply for each timeout setting
foreach my $timeout (qw{ kill lock }) {
if (@{$idleTimeoutsOverride{$timeout}} == 0) {
# zero override, we'll use the global setting,
# $idleTimeout{$timeout} is already inited to the global setting
osh_debug("idle_timeout: no override for $timeout, using global setting");
}
elsif (@{$idleTimeoutsOverride{$timeout}} == 1) {
# exactly one match, use it
$idleTimeout{$timeout} = $idleTimeoutsOverride{$timeout}[0];
osh_debug("idle_timeout: exactly one override for $timeout, using it");
}
else {
osh_debug("idle_timeout: more than one override for $timeout, using the most restrictive one");
# more than one match, so we add the global setting to the pile
push @{$idleTimeoutsOverride{$timeout}}, $idleTimeout{$timeout};
# and choose the most restrictive one (lowest positive integer)
$idleTimeout{$timeout} = (sort { $a <=> $b } grep { $_ > 0 } @{$idleTimeoutsOverride{$timeout}})[0];
}
osh_debug("idle_timeout: finally using " . $idleTimeout{$timeout} . " for $timeout");
}
# adjust the ttyrec cmdline with these parameters
$ttyrec_fnret = OVH::Bastion::build_ttyrec_cmdline_part2of2(
input => $ttyrec_fnret->value,
idleLockTimeout => $idleTimeout{'lock'},
idleKillTimeout => $idleTimeout{'kill'}
);
main_exit(OVH::Bastion::EXIT_TTYREC_CMDLINE_FAILED, "ttyrec_failed", $ttyrec_fnret->msg) if !$ttyrec_fnret;
@ttyrec = @{$ttyrec_fnret->value->{'cmd'}};
my @keysToTry; my @keysToTry;
print " will try the following accesses you have: \n" unless $quiet; print " will try the following accesses you have: \n" unless $quiet;
foreach my $access (@accessList) { foreach my $access (@accessList) {

View file

@ -119,6 +119,9 @@ use constant {
OPT_ACCOUNT_OSH_ONLY => 'osh_only', OPT_ACCOUNT_OSH_ONLY => 'osh_only',
OPT_ACCOUNT_MAX_INACTIVE_DAYS => {key => 'max_inactive_days', public => 1}, OPT_ACCOUNT_MAX_INACTIVE_DAYS => {key => 'max_inactive_days', public => 1},
OPT_GROUP_IDLE_LOCK_TIMEOUT => {key => 'idle_lock_timeout'},
OPT_GROUP_IDLE_KILL_TIMEOUT => {key => 'idle_kill_timeout'},
}; };
########### ###########
@ -991,6 +994,19 @@ sub get_passfile {
sub build_ttyrec_cmdline { sub build_ttyrec_cmdline {
my %params = @_; my %params = @_;
my $fnret = build_ttyrec_cmdline_part1of2(%params);
$fnret or return $fnret;
# for this simple version, use global timeout values
return build_ttyrec_cmdline_part2of2(
input => $fnret->value,
idleLockTimeout => OVH::Bastion::config("idleLockTimeout")->value,
idleKillTimeout => OVH::Bastion::config("idleKillTimeout")->value
);
}
sub build_ttyrec_cmdline_part1of2 {
my %params = @_;
if (!$params{'home'}) { if (!$params{'home'}) {
return R('ERR_MISSING_PARAMETER', msg => "Missing home parameter"); return R('ERR_MISSING_PARAMETER', msg => "Missing home parameter");
@ -1041,11 +1057,6 @@ sub build_ttyrec_cmdline {
} }
# forge ttyrec command # forge ttyrec command
my $idleKillTimeout = OVH::Bastion::config('idleKillTimeout')->value;
my $idleLockTimeout = OVH::Bastion::config('idleLockTimeout')->value;
my $warnBeforeLockSeconds = OVH::Bastion::config('warnBeforeLockSeconds')->value;
my $warnBeforeKillSeconds = OVH::Bastion::config('warnBeforeKillSeconds')->value;
my @ttyrec = ('ttyrec', '-f', $saveFile, '-F', $saveFileFormat); my @ttyrec = ('ttyrec', '-f', $saveFile, '-F', $saveFileFormat);
push @ttyrec, '-v' if $params{'debug'}; push @ttyrec, '-v' if $params{'debug'};
push @ttyrec, '-T', 'always' if $params{'tty'}; push @ttyrec, '-T', 'always' if $params{'tty'};
@ -1060,19 +1071,45 @@ sub build_ttyrec_cmdline {
osh_debug("Account is immune to idle, not adding ttyrec commandline parameters"); osh_debug("Account is immune to idle, not adding ttyrec commandline parameters");
} }
else { else {
push @ttyrec, '-k', $idleKillTimeout if $idleKillTimeout; my $warnBeforeLockSeconds = OVH::Bastion::config('warnBeforeLockSeconds')->value;
push @ttyrec, '-t', $idleLockTimeout if $idleLockTimeout; my $warnBeforeKillSeconds = OVH::Bastion::config('warnBeforeKillSeconds')->value;
push @ttyrec, '-s', "To unlock, use '--osh unlock' from another console" if $idleLockTimeout;
push @ttyrec, '--warn-before-lock', $warnBeforeLockSeconds if $warnBeforeLockSeconds; push @ttyrec, '--warn-before-lock', $warnBeforeLockSeconds if $warnBeforeLockSeconds;
push @ttyrec, '--warn-before-kill', $warnBeforeKillSeconds if $warnBeforeKillSeconds; push @ttyrec, '--warn-before-kill', $warnBeforeKillSeconds if $warnBeforeKillSeconds;
} }
my $ttyrecAdditionalParameters = OVH::Bastion::config('ttyrecAdditionalParameters')->value;
push @ttyrec, @$ttyrecAdditionalParameters if @$ttyrecAdditionalParameters;
return R('OK', value => {saveFile => $saveFile, cmd => \@ttyrec}); return R('OK', value => {saveFile => $saveFile, cmd => \@ttyrec});
} }
# call this after build_ttyrec_cmdline_part1of2, don't forget to
# pass part1of2's value output to part2of2's 'input' parameter
sub build_ttyrec_cmdline_part2of2 {
my %params = @_;
my $input = $params{'input'};
if (!$input) {
return R('ERR_MISSING_PARAMETER', msg => "Missing 'input' parameter in build_ttyrec_cmdline_part2of2");
}
if (!$input->{'cmd'}) {
return R('ERR_MISSING_PARAMETER', msg => "Missing 'input->cmd' parameter in build_ttyrec_cmdline_part2of2");
}
my @cmd = @{$input->{'cmd'}};
my $idleLockTimeout = $params{'idleLockTimeout'};
my $idleKillTimeout = $params{'idleKillTimeout'};
push @cmd, '-k', $idleKillTimeout if $idleKillTimeout;
push @cmd, '-t', $idleLockTimeout if $idleLockTimeout;
push @cmd, '-s', "To unlock, use '--osh unlock' from another console" if $idleLockTimeout;
my $ttyrecAdditionalParameters = OVH::Bastion::config('ttyrecAdditionalParameters')->value;
push @cmd, @$ttyrecAdditionalParameters if @$ttyrecAdditionalParameters;
$input->{'cmd'} = \@cmd;
return R('OK', value => $input);
}
sub do_pamtester { sub do_pamtester {
my %params = @_; my %params = @_;
my $sysself = $params{'sysself'}; my $sysself = $params{'sysself'};

View file

@ -842,36 +842,42 @@ sub is_access_granted {
# 3/3 fill up keys and other metadata info (mfa, idle lock/kill timeout) if asked to # 3/3 fill up keys and other metadata info (mfa, idle lock/kill timeout) if asked to
if ($details) { if ($details) {
foreach my $access (@grants) { foreach my $access (@grants) {
undef $fnret; my %data;
my $mfaFnret;
if ($access->{'type'} =~ /^group/ and $access->{'group'}) { if ($access->{'type'} =~ /^group/ and $access->{'group'}) {
$fnret = OVH::Bastion::get_group_keys( $data{'keys'} = OVH::Bastion::get_group_keys(
group => $access->{'group'}, group => $access->{'group'},
listOnly => $listOnly, listOnly => $listOnly,
noexec => $noexec, noexec => $noexec,
forceKey => $access->{'forceKey'} forceKey => $access->{'forceKey'}
); );
$mfaFnret = OVH::Bastion::group_config(key => "mfa_required", group => $access->{'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'});
} }
elsif ($access->{'type'} =~ /^personal/) { elsif ($access->{'type'} =~ /^personal/) {
$fnret = OVH::Bastion::get_personal_account_keys( $data{'keys'} = OVH::Bastion::get_personal_account_keys(
account => $sysaccount, account => $sysaccount,
listOnly => $listOnly, listOnly => $listOnly,
noexec => $noexec, noexec => $noexec,
forceKey => $access->{'forceKey'} forceKey => $access->{'forceKey'}
); );
$mfaFnret = OVH::Bastion::account_config(key => "personal_egress_mfa_required", account => $sysaccount); $data{'mfa'} =
OVH::Bastion::account_config(key => "personal_egress_mfa_required", account => $sysaccount);
} }
else { else {
# unknown access type? no key! # unknown access type? no key!
warn_syslog("Unknown access type '" . $access->{'type'} . "' found, ignoring"); warn_syslog("Unknown access type '" . $access->{'type'} . "' found, ignoring");
} }
if ($fnret) { if ($data{'keys'}) {
# TODO implement $access->{forceKey} check to include only the proper key # TODO implement $access->{forceKey} check to include only the proper key
$access->{'keys'} = $fnret->value->{'keys'}; $access->{'keys'} = $data{'keys'}->value->{'keys'};
$access->{'sortedKeys'} = $fnret->value->{'sortedKeys'}; $access->{'sortedKeys'} = $data{'keys'}->value->{'sortedKeys'};
$access->{'mfaRequired'} = $mfaFnret->value if $mfaFnret; $access->{'mfaRequired'} = $data{'mfa'}->value if $data{'mfa'};
$access->{'idleLockTimeout'} = $data{'idle_lock_timeout'}->value if $data{'idle_lock_timeout'};
$access->{'idleKillTimeout'} = $data{'idle_kill_timeout'}->value if $data{'idle_kill_timeout'};
} }
} }
} }

View file

@ -2,6 +2,7 @@
# vim: set filetype=perl ts=4 sw=4 sts=4 et: # vim: set filetype=perl ts=4 sw=4 sts=4 et:
use common::sense; use common::sense;
use Test::More; use Test::More;
use Test::Deep;
use File::Basename; use File::Basename;
use lib dirname(__FILE__) . '/../../lib/perl'; use lib dirname(__FILE__) . '/../../lib/perl';
@ -46,6 +47,9 @@ OVH::Bastion::load_configuration(
], ],
bastionName => "mock", bastionName => "mock",
idleLockTimeout => 17,
idleKillTimeout => 29,
# all options below are bool, we'll test for their normalization # all options below are bool, we'll test for their normalization
enableSyslog => 1, enableSyslog => 1,
enableGlobalAccessLog => JSON::true, enableGlobalAccessLog => JSON::true,
@ -60,6 +64,98 @@ OVH::Bastion::load_configuration(
); );
# TESTS # TESTS
my $fnret;
$fnret = OVH::Bastion::build_ttyrec_cmdline(
ip => "127.0.0.1",
port => 7979,
user => "randomuser",
account => "bastionuser",
uniqid => 'cafed00dcafe',
home => "/home/randomuser",
);
cmp_deeply(
$fnret->value->{'saveFile'},
re(
qr{^\Q/home/randomuser/ttyrec/127.0.0.1/20\E\d\d-\d\d-\d\d.\d\d\-\d\d\-\d\d\.\d{6}\Q.cafed00dcafe.bastionuser.randomuser.127.0.0.1.7979.ttyrec\E$}
),
"build_ttyrec_cmdline saveFile"
);
cmp_deeply(
$fnret->value->{'cmd'},
[
'ttyrec',
'-f',
$fnret->value->{'saveFile'},
'-F',
'/home/randomuser/ttyrec/127.0.0.1/%Y-%m-%d.%H-%M-%S.#usec#.cafed00dcafe.bastionuser.randomuser.127.0.0.1.7979.ttyrec',
'-k',
29,
'-t',
17,
'-s',
"To unlock, use '--osh unlock' from another console",
'-k',
29,
],
"build_ttyrec_cmdline cmd"
);
$fnret = OVH::Bastion::build_ttyrec_cmdline_part1of2(
ip => "127.0.0.1",
port => 7979,
user => "randomuser",
account => "bastionuser",
uniqid => 'cafed00dcafe',
home => "/home/randomuser",
);
cmp_deeply(
$fnret->value->{'saveFile'},
re(
qr{^\Q/home/randomuser/ttyrec/127.0.0.1/20\E\d\d-\d\d-\d\d.\d\d\-\d\d\-\d\d\.\d{6}\Q.cafed00dcafe.bastionuser.randomuser.127.0.0.1.7979.ttyrec\E$}
),
"build_ttyrec_cmdline_part1of2 saveFile"
);
cmp_deeply(
$fnret->value->{'cmd'},
[
'ttyrec',
'-f',
$fnret->value->{'saveFile'},
'-F',
'/home/randomuser/ttyrec/127.0.0.1/%Y-%m-%d.%H-%M-%S.#usec#.cafed00dcafe.bastionuser.randomuser.127.0.0.1.7979.ttyrec'
],
"build_ttyrec_cmdline_part1of2 cmd"
);
$fnret = OVH::Bastion::build_ttyrec_cmdline_part2of2(
input => $fnret->value,
idleKillTimeout => 88,
idleLockTimeout => 99,
);
cmp_deeply(
$fnret->value->{'saveFile'},
re(
qr{^\Q/home/randomuser/ttyrec/127.0.0.1/20\E\d\d-\d\d-\d\d.\d\d\-\d\d\-\d\d\.\d{6}\Q.cafed00dcafe.bastionuser.randomuser.127.0.0.1.7979.ttyrec\E$}
),
"build_ttyrec_cmdline_part2of2 saveFile"
);
cmp_deeply(
$fnret->value->{'cmd'},
[
'ttyrec',
'-f',
$fnret->value->{'saveFile'},
'-F',
'/home/randomuser/ttyrec/127.0.0.1/%Y-%m-%d.%H-%M-%S.#usec#.cafed00dcafe.bastionuser.randomuser.127.0.0.1.7979.ttyrec',
'-k',
88,
'-t',
99,
'-s',
"To unlock, use '--osh unlock' from another console"
],
"build_ttyrec_cmdline_part2of2 cmd"
);
is(OVH::Bastion::config("bastionName")->value, "mock", "bastion name is mocked"); is(OVH::Bastion::config("bastionName")->value, "mock", "bastion name is mocked");