new account option: --pubkey-auth-optional, to allow ingress login with or without pubkey when pam is required

This commit is contained in:
Christophe Crochet 2021-09-29 16:16:07 +02:00 committed by Stéphane Lesimple
parent 4d3ee1b99d
commit d85298f229
18 changed files with 120 additions and 97 deletions

View file

@ -872,7 +872,7 @@ if [ "$nothing" = 0 ]; then
at_least_one_error=0
for group in bastion-users \
mfa-password-reqd mfa-password-bypass mfa-password-configd \
mfa-totp-reqd mfa-totp-bypass mfa-totp-configd bastion-nopam mfa-any
mfa-totp-reqd mfa-totp-bypass mfa-totp-configd bastion-nopam osh-pubkey-auth-optional
do
if getent group "$group" >/dev/null 2>&1; then
:

View file

@ -280,45 +280,45 @@ foreach my $tuple (@modify) {
}
}
}
elsif ($key eq 'mfa-any') {
$fnret = OVH::Bastion::is_user_in_group(user => $account, group => OVH::Bastion::MFA_ANY_GROUP);
elsif ($key eq 'pubkey-auth-optional') {
$fnret = OVH::Bastion::is_user_in_group(user => $account, group => OVH::Bastion::OSH_PUBKEY_AUTH_OPTIONAL_GROUP);
if ($value eq 'yes') {
{
osh_info "Setting authentication requirements to pubkey OR pam (if a password/TOTP is set) for this account...";
osh_info "Making public key authentication optional for this account...";
if ($fnret) {
osh_info "... no change was required";
$result{$jsonkey} = R('OK_NO_CHANGE');
last;
}
$fnret = OVH::Bastion::sys_addmembertogroup(user => $account, group => OVH::Bastion::MFA_ANY_GROUP, noisy_stderr => 1);
$fnret = OVH::Bastion::sys_addmembertogroup(user => $account, group => OVH::Bastion::OSH_PUBKEY_AUTH_OPTIONAL_GROUP, noisy_stderr => 1);
if (!$fnret) {
osh_warn "... error while setting the alternative authentication option";
osh_warn "... error while setting the optional pubkey option";
$result{$jsonkey} = R('ERR_ADDING_TO_GROUP');
last;
}
osh_info "... done, this account can now authenticate with either pubkey or pam (if a password/TOTP is set)";
osh_info "... done, this account can now authenticate with or without a pubkey if a password/TOTP is set";
$result{$jsonkey} = R('OK');
}
}
elsif ($value eq 'no') {
{
osh_info "Setting authentication to pubkey AND pam (if a password/TOTP is set) for this account...";
osh_info "Making pubkey authentication mandatory for this account...";
if (!$fnret) {
osh_info "... no change was required";
$result{$jsonkey} = R('OK_NO_CHANGE');
last;
}
$fnret = OVH::Bastion::sys_delmemberfromgroup(user => $account, group => OVH::Bastion::MFA_ANY_GROUP, noisy_stderr => 1);
$fnret = OVH::Bastion::sys_delmemberfromgroup(user => $account, group => OVH::Bastion::OSH_PUBKEY_AUTH_OPTIONAL_GROUP, noisy_stderr => 1);
if (!$fnret) {
osh_warn "... error while removing the alternative authentication option";
osh_warn "... error while removing the optional pubkey option";
$result{$jsonkey} = R('ERR_REMOVING_FROM_GROUP');
last;
}
osh_info "... done, this account now requires to authenticate with pubkey AND pam (if a password/TOTP is set)";
osh_info "... done, this account now requires a pubkey to authenticate";
$result{$jsonkey} = R('OK');
}
}

View file

@ -267,8 +267,8 @@ if (OVH::Bastion::is_auditor(account => $self)) {
$ret{'pam_auth_bypass'} = OVH::Bastion::is_user_in_group(user => $account, group => OVH::Bastion::PAM_AUTH_BYPASS_GROUP) ? 1 : 0;
osh_info "- PAM authentication bypass is " . ($ret{'pam_auth_bypass'} ? colored('enabled', 'green') : colored('disabled', 'blue'));
$ret{'mfa_any'} = OVH::Bastion::is_user_in_group(user => $account, group => OVH::Bastion::MFA_ANY_GROUP) ? 1 : 0;
osh_info "- Alternative authentication logic (allow both pubkey alone and PAM alone) is " . ($ret{'mfa_any'} ? colored('enabled', 'green') : colored('disabled', 'blue'));
$ret{'pubkey_auth_optional'} = OVH::Bastion::is_user_in_group(user => $account, group => OVH::Bastion::OSH_PUBKEY_AUTH_OPTIONAL_GROUP) ? 1 : 0;
osh_info "- Optional public key authentication is " . ($ret{'pubkey_auth_optional'} ? colored('enabled', 'green') : colored('disabled', 'blue'));
$ret{'personal_egress_mfa_required'} = OVH::Bastion::account_config(account => $account, key => "personal_egress_mfa_required")->value;
$ret{'personal_egress_mfa_required'} ||= 'none'; # no config means no mfa

View file

@ -131,14 +131,14 @@ foreach my $account (sort keys %$accounts) {
$states{'can_connect'} = ($states{'is_active'} && !$states{'is_expired'}) ? 1 : 0;
$states{'mfa_password_required'} = OVH::Bastion::is_user_in_group(user => $account, group => OVH::Bastion::MFA_PASSWORD_REQUIRED_GROUP) ? 1 : 0;
$states{'mfa_password_configured'} = OVH::Bastion::is_user_in_group(user => $account, group => OVH::Bastion::MFA_PASSWORD_CONFIGURED_GROUP) ? 1 : 0;
$states{'mfa_password_bypass'} = OVH::Bastion::is_user_in_group(user => $account, group => OVH::Bastion::MFA_PASSWORD_BYPASS_GROUP) ? 1 : 0;
$states{'mfa_totp_required'} = OVH::Bastion::is_user_in_group(user => $account, group => OVH::Bastion::MFA_TOTP_REQUIRED_GROUP) ? 1 : 0;
$states{'mfa_totp_configured'} = OVH::Bastion::is_user_in_group(user => $account, group => OVH::Bastion::MFA_TOTP_CONFIGURED_GROUP) ? 1 : 0;
$states{'mfa_totp_bypass'} = OVH::Bastion::is_user_in_group(user => $account, group => OVH::Bastion::MFA_TOTP_BYPASS_GROUP) ? 1 : 0;
$states{'pam_auth_bypass'} = OVH::Bastion::is_user_in_group(user => $account, group => OVH::Bastion::PAM_AUTH_BYPASS_GROUP) ? 1 : 0;
$states{'mfa_any'} = OVH::Bastion::is_user_in_group(user => $account, group => OVH::Bastion::MFA_ANY_GROUP) ? 1 : 0;
$states{'mfa_password_required'} = OVH::Bastion::is_user_in_group(user => $account, group => OVH::Bastion::MFA_PASSWORD_REQUIRED_GROUP) ? 1 : 0;
$states{'mfa_password_configured'} = OVH::Bastion::is_user_in_group(user => $account, group => OVH::Bastion::MFA_PASSWORD_CONFIGURED_GROUP) ? 1 : 0;
$states{'mfa_password_bypass'} = OVH::Bastion::is_user_in_group(user => $account, group => OVH::Bastion::MFA_PASSWORD_BYPASS_GROUP) ? 1 : 0;
$states{'mfa_totp_required'} = OVH::Bastion::is_user_in_group(user => $account, group => OVH::Bastion::MFA_TOTP_REQUIRED_GROUP) ? 1 : 0;
$states{'mfa_totp_configured'} = OVH::Bastion::is_user_in_group(user => $account, group => OVH::Bastion::MFA_TOTP_CONFIGURED_GROUP) ? 1 : 0;
$states{'mfa_totp_bypass'} = OVH::Bastion::is_user_in_group(user => $account, group => OVH::Bastion::MFA_TOTP_BYPASS_GROUP) ? 1 : 0;
$states{'pam_auth_bypass'} = OVH::Bastion::is_user_in_group(user => $account, group => OVH::Bastion::PAM_AUTH_BYPASS_GROUP) ? 1 : 0;
$states{'pubkey_auth_optional'} = OVH::Bastion::is_user_in_group(user => $account, group => OVH::Bastion::OSH_PUBKEY_AUTH_OPTIONAL_GROUP) ? 1 : 0;
if ($fnretPassword) {
$states{"password_$_"} = $fnretPassword->value->{$account}{$_} for (keys %{$fnretPassword->value->{$account}});
@ -160,7 +160,7 @@ foreach my $account (sort keys %$accounts) {
push @mfaTOTP, 'bypass' if $states{'mfa_totp_bypass'};
osh_info sprintf(
"%-18s %6d active:%-12s expired:%-12s can_connect:%-12s already_seen:%-12s mfa_password:%-25s mfa_totp:%-25s pam_bypass:%-12s mfa_any:%-12s pass_status:%-15s pass_changed:%-10s pass_min_days:%-3d pass_max_days:%-3d pass_warn_days:%-3d %s\n",
"%-18s %6d active:%-12s expired:%-12s can_connect:%-12s already_seen:%-12s mfa_password:%-25s mfa_totp:%-25s pam_bypass:%-12s pubkey_auth_optional:%-12s pass_status:%-15s pass_changed:%-10s pass_min_days:%-3d pass_max_days:%-3d pass_warn_days:%-3d %s\n",
$account,
$accounts->{$account}{'uid'},
tristate2str($states{'is_active'}),
@ -169,8 +169,8 @@ foreach my $account (sort keys %$accounts) {
tristate2str($states{'already_seen_before'}),
@mfaPassword ? colored(join(',', @mfaPassword), 'green') : colored('-', 'blue'),
@mfaTOTP ? colored(join(',', @mfaTOTP), 'green') : colored('-', 'blue'),
tristate2str($states{'pam_auth_bypass'}, 1),
tristate2str($states{'mfa_any'}, 1),
tristate2str($states{'pam_auth_bypass'}, 1),
tristate2str($states{'pubkey_auth_optional'}, 1),
(
$states{'password_password'} eq 'locked'
? colored('locked', 'blue')

View file

@ -23,7 +23,7 @@ my $remainingOptions = OVH::Bastion::Plugin::begin(
"idle-ignore=s" => \$modify{'idle-ignore'},
"max-inactive-days=i" => \$modify{'max-inactive-days'},
"osh-only=s" => \$modify{'osh-only'},
"mfa-any=s" => \$modify{'mfa-any'},
"pubkey-auth-optional=s" => \$modify{'pubkey-auth-optional'},
},
helptext => <<'EOF',
Modify an account configuration
@ -55,10 +55,12 @@ Usage: --osh SCRIPT_NAME --account ACCOUNT [--option value [--option value [...]
Setting this option to zero disables account expiration. Setting this option to -1 removes this account
expiration policy, i.e. the global bastion setting will apply.
--osh-only yes|no If enabled, this account can only use ``--osh`` commands, and can't connect anywhere through the bastion
--mfa-any yes|no Control the ingress login requirements for pubkey and pam (when a password and/or TOTP is set).
When disabled, the user needs pubkey AND pam, this is the default.
When enabled, the user can authenticate with either pubkey OR pam.
If the account has no password/TOTP, this option has no effect, i.e: pubkey is used. Egress is not affected.
--pubkey-auth-optional yes|no Make the public key optional on ingress for the account (default is 'no').
When enabled the public key part of the authentication becomes optional when a password and/or TOTP is defined,
allowing to login with just the password/TOTP. If no password/TOTP is defined then the public key is the only way to authenticate,
because some form of authentication is always required.
When disabled, the public key is always required.
Egress is not affected.
EOF
);
@ -86,7 +88,7 @@ foreach my $key (qw{ mfa-password-required mfa-totp-required }) {
osh_exit 'ERR_INVALID_PARAMETER', "Expected '--$key yes' or '--$key no' or '--$key bypass' instead of '--$key $modify{$key}'";
}
}
foreach my $key (qw{ always-active pam-auth-bypass idle-ignore osh-only mfa-any }) {
foreach my $key (qw{ always-active pam-auth-bypass idle-ignore osh-only pubkey-auth-optional }) {
next unless $modify{$key};
if (not grep { $modify{$key} eq $_ } qw{ yes no }) {
help();

View file

@ -1,13 +1,13 @@
{
"master_only": true,
"interactive": [
"accountModify" , {"ac": ["--account"]},
"accountModify --account" , {"ac": ["<ACCOUNT>"]},
"accountModify --account \\S+" , {"ac": ["--mfa-password-required","--mfa-totp-required","--pam-auth-bypass","--always-active","--egress-strict-host-key-checking","--personal-egress-mfa-required","--idle-ignore","--mfa-any"]},
"accountModify --account \\S+ .*(--mfa-password-required|--mfa-totp-required)" , {"ac": ["yes","no","bypass"]},
"accountModify --account \\S+ .*(--pam-auth-bypass|--mfa-auth-bypass|--always-active|idle-ignore|--mfa-any)", {"ac": ["yes","no"]},
"accountModify --account \\S+ .*(--egress-strict-host-key-checking)" , {"ac": ["yes","accept-new","no","ask","default","bypass"]},
"accountModify --account \\S+ .*(--personal-egress-mfa-required)" , {"ac": ["password","totp","any","none"]},
"accountModify --account \\S+ .*(yes|accept-new|no|bypass|ask|default|totp|password|none)" , {"ac": ["--mfa-password-required","--mfa-totp-required","--pam-auth-bypass","--always-active","--egress-strict-host-key-checking","--personal-egress-mfa-required","--idle-ignore","-mfa-any","<enter>"]}
"accountModify" , {"ac": ["--account"]},
"accountModify --account" , {"ac": ["<ACCOUNT>"]},
"accountModify --account \\S+" , {"ac": ["--mfa-password-required","--mfa-totp-required","--pam-auth-bypass","--always-active","--egress-strict-host-key-checking","--personal-egress-mfa-required","--idle-ignore","--pubkey-auth-optional"]},
"accountModify --account \\S+ .*(--mfa-password-required|--mfa-totp-required)" , {"ac": ["yes","no","bypass"]},
"accountModify --account \\S+ .*(--pam-auth-bypass|--mfa-auth-bypass|--always-active|idle-ignore|--pubkey-auth-optional)", {"ac": ["yes","no"]},
"accountModify --account \\S+ .*(--egress-strict-host-key-checking)" , {"ac": ["yes","accept-new","no","ask","default","bypass"]},
"accountModify --account \\S+ .*(--personal-egress-mfa-required)" , {"ac": ["password","totp","any","none"]},
"accountModify --account \\S+ .*(yes|accept-new|no|bypass|ask|default|totp|password|none)" , {"ac": ["--mfa-password-required","--mfa-totp-required","--pam-auth-bypass","--always-active","--egress-strict-host-key-checking","--personal-egress-mfa-required","--idle-ignore","--pubkey-auth-optional","<enter>"]}
]
}

View file

@ -36,7 +36,7 @@ Output example
~ - Additional TOTP authentication bypass is disabled for this account
~ - Additional TOTP authentication is disabled
~ - PAM authentication bypass is disabled
~ - Alternative authentication logic (allow both pubkey alone and PAM alone) is disabled
~ - Optional public key authentication is disabled
~ - MFA policy on personal accesses (using personal keys) on egress side is: password
~ Account PAM UNIX password information (used for password MFA):

View file

@ -60,7 +60,7 @@ Output example
~ - Additional TOTP authentication bypass is disabled for this account
~ - Additional TOTP authentication is disabled
~ - PAM authentication bypass is disabled
~ - Alternative authentication logic (allow both pubkey alone and PAM alone) is disabled
~ - Optional public key authentication is disabled
~ - MFA policy on personal accesses (using personal keys) on egress side is: password
~ Account PAM UNIX password information (used for password MFA):

View file

@ -69,10 +69,12 @@ Modify an account configuration
If enabled, this account can only use ``--osh`` commands, and can't connect anywhere through the bastion
.. option:: --mfa-any yes|no
.. option:: --pubkey-auth-optional yes|no
Control the ingress login requirements for pubkey and pam (when a password and/or TOTP is set).
Make the public key optional on ingress for the account (default is 'no').
When disabled, the user needs pubkey AND pam, this is the default.
When enabled, the user can authenticate with either pubkey OR pam.
If the account has no password/TOTP, this option has no effect, i.e: pubkey is used. Egress is not affected.
When enabled the public key part of the authentication becomes optional when a password and/or TOTP is defined,
allowing to login with just the password/TOTP. If no password/TOTP is defined then the public key is the only way to authenticate,
because some form of authentication is always required.
When disabled, the public key is always required.
Egress is not affected.

View file

@ -129,9 +129,9 @@ UsePAM yes
# Unconditionally skip PAM auth for members of the bastion-nopam group
Match Group bastion-nopam
AuthenticationMethods publickey
# if in one of the mfa groups AND the mfa-any group, use publickey OR pam
Match Group mfa-totp-configd,mfa-password-configd Group mfa-any
AuthenticationMethods publickey keyboard-interactive:pam
# if in one of the mfa groups AND the osh-pubkey-auth-optional group, use publickey+pam OR pam
Match Group mfa-totp-configd,mfa-password-configd Group osh-pubkey-auth-optional
AuthenticationMethods publickey,keyboard-interactive:pam keyboard-interactive:pam
# if in one of the mfa groups, use publickey AND pam
Match Group mfa-totp-configd,mfa-password-configd
AuthenticationMethods publickey,keyboard-interactive:pam

View file

@ -129,9 +129,9 @@ UsePAM yes
# Unconditionally skip PAM auth for members of the bastion-nopam group
Match Group bastion-nopam
AuthenticationMethods publickey
# if in one of the mfa groups AND the mfa-any group, use publickey OR pam
Match Group mfa-totp-configd,mfa-password-configd Group mfa-any
AuthenticationMethods publickey keyboard-interactive:pam
# if in one of the mfa groups AND the osh-pubkey-auth-optional group, use publickey+pam OR pam
Match Group mfa-totp-configd,mfa-password-configd Group osh-pubkey-auth-optional
AuthenticationMethods publickey,keyboard-interactive:pam keyboard-interactive:pam
# if in one of the mfa groups, use publickey AND pam
Match Group mfa-totp-configd,mfa-password-configd
AuthenticationMethods publickey,keyboard-interactive:pam

View file

@ -133,9 +133,9 @@ UsePAM yes
# Unconditionally skip PAM auth for members of the bastion-nopam group
Match Group bastion-nopam
AuthenticationMethods publickey
# if in one of the mfa groups AND the mfa-any group, use publickey OR pam
Match Group mfa-totp-configd,mfa-password-configd Group mfa-any
AuthenticationMethods publickey keyboard-interactive:pam
# if in one of the mfa groups AND the osh-pubkey-auth-optional group, use publickey+pam OR pam
Match Group mfa-totp-configd,mfa-password-configd Group osh-pubkey-auth-optional
AuthenticationMethods publickey,keyboard-interactive:pam keyboard-interactive:pam
# if in one of the mfa groups, use publickey AND pam
Match Group mfa-totp-configd,mfa-password-configd
AuthenticationMethods publickey,keyboard-interactive:pam

View file

@ -133,9 +133,9 @@ UsePAM yes
# Unconditionally skip PAM auth for members of the bastion-nopam group
Match Group bastion-nopam
AuthenticationMethods publickey
# if in one of the mfa groups AND the mfa-any group, use publickey OR pam
Match Group mfa-totp-configd,mfa-password-configd Group mfa-any
AuthenticationMethods publickey keyboard-interactive:pam
# if in one of the mfa groups AND the osh-pubkey-auth-optional group, use publickey+pam OR pam
Match Group mfa-totp-configd,mfa-password-configd Group osh-pubkey-auth-optional
AuthenticationMethods publickey,keyboard-interactive:pam keyboard-interactive:pam
# if in one of the mfa groups, use publickey AND pam
Match Group mfa-totp-configd,mfa-password-configd
AuthenticationMethods publickey,keyboard-interactive:pam

View file

@ -136,9 +136,9 @@ UsePAM yes
# Unconditionally skip PAM auth for members of the bastion-nopam group
Match Group bastion-nopam
AuthenticationMethods publickey
# if in one of the mfa groups AND the mfa-any group, use publickey OR pam
Match Group mfa-totp-configd,mfa-password-configd Group mfa-any
AuthenticationMethods publickey keyboard-interactive:pam
# if in one of the mfa groups AND the osh-pubkey-auth-optional group, use publickey+pam OR pam
Match Group mfa-totp-configd,mfa-password-configd Group osh-pubkey-auth-optional
AuthenticationMethods publickey,keyboard-interactive:pam keyboard-interactive:pam
# if in one of the mfa groups, use publickey AND pam
Match Group mfa-totp-configd,mfa-password-configd
AuthenticationMethods publickey,keyboard-interactive:pam

View file

@ -136,9 +136,9 @@ UsePAM yes
# Unconditionally skip PAM auth for members of the bastion-nopam group
Match Group bastion-nopam
AuthenticationMethods publickey
# if in one of the mfa groups AND the mfa-any group, use publickey OR pam
Match Group mfa-totp-configd,mfa-password-configd Group mfa-any
AuthenticationMethods publickey keyboard-interactive:pam
# if in one of the mfa groups AND the osh-pubkey-auth-optional group, use publickey+pam OR pam
Match Group mfa-totp-configd,mfa-password-configd Group osh-pubkey-auth-optional
AuthenticationMethods publickey,keyboard-interactive:pam keyboard-interactive:pam
# if in one of the mfa groups, use publickey AND pam
Match Group mfa-totp-configd,mfa-password-configd
AuthenticationMethods publickey,keyboard-interactive:pam

View file

@ -126,9 +126,9 @@ UsePAM yes
# Unconditionally skip PAM auth for members of the bastion-nopam group
Match Group bastion-nopam
AuthenticationMethods publickey
# if in one of the mfa groups AND the mfa-any group, use publickey OR pam
Match Group mfa-totp-configd,mfa-password-configd Group mfa-any
AuthenticationMethods publickey keyboard-interactive:pam
# if in one of the mfa groups AND the osh-pubkey-auth-optional group, use publickey+pam OR pam
Match Group mfa-totp-configd,mfa-password-configd Group osh-pubkey-auth-optional
AuthenticationMethods publickey,keyboard-interactive:pam keyboard-interactive:pam
# if in one of the mfa groups, use publickey AND pam
Match Group mfa-totp-configd,mfa-password-configd
AuthenticationMethods publickey,keyboard-interactive:pam

View file

@ -96,14 +96,14 @@ use constant {
};
use constant {
MFA_PASSWORD_REQUIRED_GROUP => 'mfa-password-reqd',
MFA_PASSWORD_CONFIGURED_GROUP => 'mfa-password-configd',
MFA_PASSWORD_BYPASS_GROUP => 'mfa-password-bypass',
MFA_TOTP_REQUIRED_GROUP => 'mfa-totp-reqd',
MFA_TOTP_CONFIGURED_GROUP => 'mfa-totp-configd',
MFA_TOTP_BYPASS_GROUP => 'mfa-totp-bypass',
PAM_AUTH_BYPASS_GROUP => 'bastion-nopam',
MFA_ANY_GROUP => 'mfa-any',
MFA_PASSWORD_REQUIRED_GROUP => 'mfa-password-reqd',
MFA_PASSWORD_CONFIGURED_GROUP => 'mfa-password-configd',
MFA_PASSWORD_BYPASS_GROUP => 'mfa-password-bypass',
MFA_TOTP_REQUIRED_GROUP => 'mfa-totp-reqd',
MFA_TOTP_CONFIGURED_GROUP => 'mfa-totp-configd',
MFA_TOTP_BYPASS_GROUP => 'mfa-totp-bypass',
PAM_AUTH_BYPASS_GROUP => 'bastion-nopam',
OSH_PUBKEY_AUTH_OPTIONAL_GROUP => 'osh-pubkey-auth-optional',
TOTP_FILENAME => '.otp',
TOTP_BASEDIR => '/var/otp',

View file

@ -381,16 +381,18 @@ testsuite_mfa()
success a0_remove_mfa_req_a4_dupe $a0 --osh accountModify --account $account4 --pam-auth-bypass no --mfa-totp-required no --mfa-password-required no
json .error_code OK .command accountModify .value.pam_auth_bypass.error_code OK_NO_CHANGE .value.mfa_totp_required.error_code OK_NO_CHANGE .value.mfa_password_required.error_code OK_NO_CHANGE
# remove totp from account4 to simplify the following mfa-any tests
# pubkey-auth-optional
# remove totp from account4 to simplify the following tests
grant accountMFAResetTOTP
success mfa a0_nototp_a4 $a0 --osh accountMFAResetTOTP --account $account4
success a0_nototp_a4 $a0 --osh accountMFAResetTOTP --account $account4
json .command accountMFAResetTOTP .error_code OK
revoke accountMFAResetTOTP
# no mfa-any, success with pubkey and password
script mfa a4_no_mfaany_login_pubkey_pam "echo 'set timeout 30; \
# pubkey-auth-optional disabled: success with pubkey and password
script a4_no_pubkeyauthoptional_login_pubkey_pam "echo 'set timeout 30; \
spawn $a4 --osh groupList; \
expect \"word:\" { sleep 0.2; send \"$a4_password\\n\"; }; \
expect eof; \
@ -401,8 +403,8 @@ testsuite_mfa()
contain REGEX 'Password:|Password for'
json .command groupList .error_code OK_EMPTY
# no mfa-any, fail with pubkey but no password (timeout)
script mfa a4_no_mfaany_login_pubkey_nopam $a4 --osh groupList
# pubkey-auth-optional disabled: fail with pubkey but no password (timeout)
script a4_no_pubkeyauthoptional_login_pubkey_nopam $a4 --osh groupList
retvalshouldbe 124
contain 'Multi-Factor Authentication enabled, an additional authentication factor is required (password).'
contain 'Your password expires on'
@ -410,27 +412,35 @@ testsuite_mfa()
contain REGEX 'Password:|Password for'
nocontain 'JSON_OUTPUT'
# no mfa-any, fail with no pubkey (never gets to ask for the password)
script mfa a4_no_mfaany_login_nopubkey_pam $a4np --osh groupList
# pubkey-auth-optional disabled: fail with no pubkey (never gets to ask for the password)
script a4_no_pubkeyauthoptional_login_nopubkey_pam $a4np --osh groupList
retvalshouldbe 255
contain 'Permission denied (publickey).'
nocontain 'password'
nocontain 'JSON_OUTPUT'
# set mfa-any on account4
success mfa a0_set_mfaany_a4 $a0 --osh accountModify --account $account4 --mfa-any yes
json .error_code OK .command accountModify .value.mfa_any.error_code OK
# set pubkey-auth-optional on account4
success a0_set_pubkeyauthoptional_a4 $a0 --osh accountModify --account $account4 --pubkey-auth-optional yes
json .error_code OK .command accountModify .value.pubkey_auth_optional.error_code OK
# set mfa-any on account4 (dupe)
success mfa a0_set_mfaany_a4_dupe $a0 --osh accountModify --account $account4 --mfa-any yes
json .error_code OK .command accountModify .value.mfa_any.error_code OK_NO_CHANGE
# set pubkey-auth-optional on account4 (dupe)
success a0_set_pubkeyauthoptional_a4_dupe $a0 --osh accountModify --account $account4 --pubkey-auth-optional yes
json .error_code OK .command accountModify .value.pubkey_auth_optional.error_code OK_NO_CHANGE
# success with pubkey but no password
success mfa a4_mfaany_login_pubkey_nopam $a4 --osh groupList
# pubkey-auth-optional enabled: success with pubkey and password
script a4_pubkeyauthoptional_login_pubkey_pam "echo 'set timeout 30; \
spawn $a4 --osh groupList; \
expect \"word:\" { sleep 0.2; send \"$a4_password\\n\"; }; \
expect eof; \
lassign [wait] pid spawnid value value; \
exit \$value' | expect -f -"
retvalshouldbe 0
contain 'Multi-Factor Authentication enabled, an additional authentication factor is required (password).'
contain REGEX 'Password:|Password for'
json .command groupList .error_code OK_EMPTY
# success with password but no pubkey
script mfa a4_mfaany_login_nopubkey_pam "echo 'set timeout 30; \
# pubkey-auth-optional enabled: success with password only
script a4_pubkeyauthoptional_login_nopubkey_pam "echo 'set timeout 30; \
spawn $a4np --osh groupList; \
expect \"word:\" { sleep 0.2; send \"$a4_password\\n\"; }; \
expect eof; \
@ -441,13 +451,22 @@ testsuite_mfa()
contain REGEX 'Password:|Password for'
json .command groupList .error_code OK_EMPTY
# unset mfa-any on account4
success mfa a0_unset_mfaany_a4 $a0 --osh accountModify --account $account4 --mfa-any no
json .error_code OK .command accountModify .value.mfa_any.error_code OK
# pubkey-auth-optional enabled: fail with pubkey only
script a4_pubkeyauthoptional_login_pubkey_nopam $a4 --osh groupList
retvalshouldbe 124
contain 'Multi-Factor Authentication enabled, an additional authentication factor is required (password).'
contain 'Your password expires on'
contain 'in 89 days'
contain REGEX 'Password:|Password for'
nocontain 'JSON_OUTPUT'
# unset pubkey-auth-optional on account4
success a0_unset_pubkeyauthoptional_a4 $a0 --osh accountModify --account $account4 --pubkey-auth-optional no
json .error_code OK .command accountModify .value.pubkey_auth_optional.error_code OK
# unset mfa-any on account4 (dupe)
success mfa a0_unset_mfaany_a4_dupe $a0 --osh accountModify --account $account4 --mfa-any no
json .error_code OK .command accountModify .value.mfa_any.error_code OK_NO_CHANGE
success a0_unset_pubkeyauthoptional_a4_dupe $a0 --osh accountModify --account $account4 --pubkey-auth-optional no
json .error_code OK .command accountModify .value.pubkey_auth_optional.error_code OK_NO_CHANGE