mirror of
https://github.com/ovh/the-bastion.git
synced 2025-09-06 13:04:22 +08:00
Merge pull request #48 from speed47/freebsd
feat: add FreeBSD in the automated tests
This commit is contained in:
commit
e1d7ef9d26
44 changed files with 618 additions and 234 deletions
37
.github/workflows/freebsd.yml
vendored
Normal file
37
.github/workflows/freebsd.yml
vendored
Normal file
|
@ -0,0 +1,37 @@
|
|||
name: FreeBSD tests
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [labeled, synchronize]
|
||||
|
||||
jobs:
|
||||
freebsd:
|
||||
runs-on: macos-latest
|
||||
name: FreeBSD
|
||||
if: contains(github.event.pull_request.labels.*.name, 'tests:full')
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Functional tests under FreeBSD
|
||||
uses: vmactions/freebsd-vm@v0.0.8
|
||||
with:
|
||||
usesh: true
|
||||
run: |
|
||||
set -ex
|
||||
freebsd-version
|
||||
mount -o acls /
|
||||
pkg install -y bash unzip rsync ca_root_nss jq fping screen flock gmake
|
||||
mkdir -p /opt/bastion
|
||||
rsync -a . /opt/bastion/
|
||||
fetch https://github.com/ovh/ovh-ttyrec/archive/master.zip
|
||||
unzip master.zip
|
||||
cd ovh-ttyrec-master/
|
||||
./configure
|
||||
gmake
|
||||
gmake install
|
||||
cd ..
|
||||
/opt/bastion/bin/admin/packages-check.sh -i
|
||||
/opt/bastion/bin/admin/install --new-install --no-wait
|
||||
ssh-keygen -t ed25519 -f id_user
|
||||
ssh-keygen -t ed25519 -f id_root
|
||||
NO_SLEEP=1 user_pubkey=$(cat id_user.pub) root_pubkey=$(cat id_root.pub) TARGET_USER=user5000 /opt/bastion/tests/functional/docker/target_role.sh
|
||||
HAS_MFA=0 HAS_MFA_PASSWORD=1 HAS_PAMTESTER=1 nocc=1 /opt/bastion/tests/functional/launch_tests_on_instance.sh 127.0.0.1 22 user5000 id_user id_root /usr/local/etc/bastion
|
22
.github/workflows/tests.yml
vendored
22
.github/workflows/tests.yml
vendored
|
@ -5,6 +5,17 @@ on:
|
|||
types: [labeled, synchronize]
|
||||
|
||||
jobs:
|
||||
tests_short:
|
||||
name: Short (Debian 10 only)
|
||||
runs-on: ubuntu-latest
|
||||
if: contains(github.event.pull_request.labels.*.name, 'tests:short')
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: run tests inside a debian10 docker
|
||||
run: tests/functional/docker/docker_build_and_run_tests.sh debian10
|
||||
env:
|
||||
DOCKER_TTY: false
|
||||
|
||||
tests_full:
|
||||
name: Full
|
||||
strategy:
|
||||
|
@ -18,14 +29,3 @@ jobs:
|
|||
run: tests/functional/docker/docker_build_and_run_tests.sh ${{ matrix.platform }}
|
||||
env:
|
||||
DOCKER_TTY: false
|
||||
|
||||
tests_short:
|
||||
name: Full on Debian 10
|
||||
runs-on: ubuntu-latest
|
||||
if: contains(github.event.pull_request.labels.*.name, 'tests:short')
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: run tests inside a debian10 docker
|
||||
run: tests/functional/docker/docker_build_and_run_tests.sh debian10
|
||||
env:
|
||||
DOCKER_TTY: false
|
||||
|
|
|
@ -542,13 +542,16 @@ sub _tocheck {
|
|||
my $gid = $stat[5];
|
||||
my $wantuser = (split / /, $tocheck{'FILEOWN'}[0])[0];
|
||||
my $wantgroup = (split / /, $tocheck{'FILEOWN'}[0])[1];
|
||||
$wantuser = $UID0 if $wantuser eq 'root';
|
||||
$wantgroup = $GID0 if $wantgroup eq 'root';
|
||||
if ($uid ne getpwnam($wantuser)) {
|
||||
_err "bad owner on file $file (got $uid but wanted $wantuser)";
|
||||
$wantuser = $UID0 if ($wantuser eq 'root' || $wantuser eq '0');
|
||||
$wantgroup = $GID0 if ($wantgroup eq 'root' || $wantgroup eq '0');
|
||||
my $wantuid = getpwnam($wantuser);
|
||||
my $wantgid = getgrnam($wantgroup);
|
||||
|
||||
if ($uid ne $wantuid) {
|
||||
_err "bad owner on file $file (got $uid but wanted $wantuid aka $wantuser)";
|
||||
}
|
||||
if ($gid ne getgrnam($wantgroup)) {
|
||||
_err "bad group on file $file (got $gid but wanted $wantgroup)";
|
||||
if ($gid ne $wantgid) {
|
||||
_err "bad group on file $file (got $gid but wanted $wantgid aka $wantgroup)";
|
||||
}
|
||||
}
|
||||
if (exists $tocheck{'SUDOERS'}) {
|
||||
|
|
|
@ -202,8 +202,8 @@ fi
|
|||
|
||||
if [ "${opt[install-fake-ttyrec]}" = 1 ]; then
|
||||
action_doing "Installing fake ttyrec (use this only for tests!)"
|
||||
if [ ! -e "/usr/bin/ttyrec" ]; then
|
||||
install -o "$UID0" -g "$GID0" -m 0755 "$basedir/tests/functional/fake_ttyrec.sh" "/usr/bin/ttyrec"
|
||||
if [ ! -e "/usr/bin/ttyrec" ] && [ ! -e "/usr/local/bin/ttyrec" ]; then
|
||||
install -o "$UID0" -g "$GID0" -m 0755 "$basedir/tests/functional/fake_ttyrec.sh" "/usr/local/bin/ttyrec"
|
||||
action_done
|
||||
else
|
||||
action_na
|
||||
|
@ -228,6 +228,8 @@ if [ "${opt[modify-ssh-config]}" = 1 ] || [ "${opt[modify-sshd-config]}" = 1 ] ;
|
|||
elif echo "$DISTRO_LIKE" | grep -q -w suse; then
|
||||
filesuffix=opensuse15
|
||||
fi
|
||||
elif [ "$OS_FAMILY" = FreeBSD ]; then
|
||||
filesuffix=freebsd
|
||||
fi
|
||||
|
||||
action_done "Will use the $filesuffix templates"
|
||||
|
@ -345,9 +347,9 @@ if [ "$nothing" = 0 ]; then
|
|||
osh-lingeringSessionsReaper osh-orphanedHomedir osh-pivGraceReaper \
|
||||
osh-protectLogs osh-rotateTtyrec osh-activeUsers
|
||||
do
|
||||
if [ -e "$ETC_DIR/cron.d/$obsolete" ]; then
|
||||
if [ -e "$CRON_DIR/$obsolete" ]; then
|
||||
at_least_one_changed=1
|
||||
rm -f "$ETC_DIR/cron.d/$obsolete"
|
||||
rm -f "$CRON_DIR/$obsolete"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
@ -381,26 +383,33 @@ if [ "$nothing" = 0 ]; then
|
|||
action_na
|
||||
fi
|
||||
|
||||
dirstocheck='bastion'
|
||||
[ "${opt[logrotate]}" = 1 ] && dirstocheck="$dirstocheck logrotate.d"
|
||||
[ "${opt[cron]}" = 1 ] && dirstocheck="$dirstocheck cron.d"
|
||||
[ "${opt[syslog-ng]}" = 1 ] && dirstocheck="$dirstocheck syslog-ng/conf.d"
|
||||
for subdir in $dirstocheck
|
||||
list="bastion"
|
||||
[ "${opt[logrotate]}" = 1 ] && list="$list logrotate"
|
||||
[ "${opt[cron]}" = 1 ] && list="$list cron"
|
||||
[ "${opt[syslog-ng]}" = 1 ] && list="$list syslog"
|
||||
for todo in $list
|
||||
do
|
||||
case "$todo" in
|
||||
bastion) subdir="bastion"; destdir="$BASTION_ETC_DIR";;
|
||||
logrotate) subdir="logrotate.d"; destdir="$ETC_DIR/logrotate.d";;
|
||||
cron) subdir="cron.d"; destdir="$CRON_DIR";;
|
||||
syslog) subdir="syslog-ng/conf.d"; destdir="$ETC_DIR/syslog-ng/conf.d";;
|
||||
*) continue;;
|
||||
esac
|
||||
# don't try to copy file in nonexistent dirs (i.e. syslog-ng if rsyslog is installed)
|
||||
# our own specific dirs have already been created above, so they exist
|
||||
action_doing "Check files in $ETC_DIR/$subdir..."
|
||||
[ -d "$ETC_DIR/$subdir" ] || continue
|
||||
action_doing "Check files in $destdir..."
|
||||
[ -d "$destdir" ] || continue
|
||||
|
||||
for file in "$basedir/etc/$subdir"/*.dist ; do
|
||||
destfile="$ETC_DIR/$subdir/$(basename "$file" .dist)"
|
||||
destfile="$destdir/$(basename "$file" .dist)"
|
||||
if [ -e "$destfile" ]; then
|
||||
# if the target already exist, check if we're asked to overwrite it
|
||||
if [ "$subdir" = "logrotate.d" ] && [ "${opt[overwrite-logrotate]}" = 1 ]; then
|
||||
if [ "$todo" = "logrotate" ] && [ "${opt[overwrite-logrotate]}" = 1 ]; then
|
||||
: # we'll overwrite
|
||||
elif [ "$subdir" = "cron.d" ] && [ "${opt[overwrite-cron]}" = 1 ]; then
|
||||
elif [ "$todo" = "cron" ] && [ "${opt[overwrite-cron]}" = 1 ]; then
|
||||
: # we'll overwrite
|
||||
elif [ "$subdir" = "syslog-ng/conf.d" ] && [ "${opt[overwrite-syslog-ng]}" = 1 ]; then
|
||||
elif [ "$todo" = "syslog" ] && [ "${opt[overwrite-syslog-ng]}" = 1 ]; then
|
||||
: # we'll overwrite
|
||||
else
|
||||
# in all other cases, don't overwrite
|
||||
|
@ -420,7 +429,7 @@ if [ "$nothing" = 0 ]; then
|
|||
action_detail "... create $destfile"
|
||||
install -o "$UID0" -g "$GID0" -m 0644 -b "$file" "$destfile"
|
||||
# actually don't do a backup for cron files: we would get double-executions...
|
||||
[ "$subdir" = "cron.d" ] && rm -f "$destfile"\~
|
||||
[ "$todo" = "cron" ] && rm -f "$destfile"\~
|
||||
|
||||
# special case if the file contains %RANDOMX%N:M%, with X between 1 and 9,
|
||||
# we replace it by a random number between N and M (for crons)
|
||||
|
@ -436,7 +445,8 @@ if [ "$nothing" = 0 ]; then
|
|||
action_detail "... we would divide by zero! fallback to a non-random random, such as $n"
|
||||
random=$n
|
||||
else
|
||||
random=$(( ( 0x$(echo "$(hostname -f) $placeholder $file" | md5sum | cut -c1-4) % (m-n) ) + n ))
|
||||
# shellcheck disable=SC2119
|
||||
random=$(( ( 0x$(echo "$(hostname -f) $placeholder $file" | md5sum_compat | cut -c1-4) % (m-n) ) + n ))
|
||||
fi
|
||||
action_detail "... in above file, replacing $placeholder by $random"
|
||||
sed_compat "s/$placeholder/$random/g" "$destfile"
|
||||
|
@ -459,7 +469,7 @@ if [ "$nothing" = 0 ]; then
|
|||
if [ ! -e $SUDOERS_FILE ] ; then
|
||||
action_error "$SUDOERS_DIR doesn't exist, is sudo installed?"
|
||||
else
|
||||
if grep -q "^#includedir $SUDOERS_DIR$" $SUDOERS_FILE ; then
|
||||
if grep -Eq "^[#@]includedir $SUDOERS_DIR$" $SUDOERS_FILE ; then
|
||||
action_na "sudoers.d already added in config"
|
||||
else
|
||||
echo '# added by the-bastion:' >> $SUDOERS_FILE
|
||||
|
@ -1187,13 +1197,13 @@ if [ "${opt[modify-umask]}" = 1 ]; then
|
|||
action_na
|
||||
fi
|
||||
|
||||
action_doing "Adjust umask in $ETC_DIR/pam.d/common-session if applicable"
|
||||
if [ -e $ETC_DIR/pam.d/common-session ]; then
|
||||
action_doing "Adjust umask in $PAM_DIR/common-session if applicable"
|
||||
if [ -e $PAM_DIR/common-session ]; then
|
||||
if ! grep -Eq '^\s*session\s+optional\s+pam_umask.so\s+umask=0?027' \
|
||||
$ETC_DIR/pam.d/common-session ; then
|
||||
$PAM_DIR/common-session ; then
|
||||
action_detail "missing umask config in file, adjusting"
|
||||
echo "# bastion config: umask needs to be at 0027" >> $ETC_DIR/pam.d/common-session
|
||||
echo "session optional pam_umask.so umask=0027" >> $ETC_DIR/pam.d/common-session
|
||||
echo "# bastion config: umask needs to be at 0027" >> $PAM_DIR/common-session
|
||||
echo "session optional pam_umask.so umask=0027" >> $PAM_DIR/common-session
|
||||
action_done
|
||||
else
|
||||
action_na "umask was already OK"
|
||||
|
@ -1205,15 +1215,16 @@ fi
|
|||
|
||||
if [ "${opt[modify-pam-sshd]}" = 1 ]; then
|
||||
action_doing "Use our template for pam.d/sshd"
|
||||
if grep -Eiq '^[[:space:]]*AuthenticationMethods[[:space:]]+publickey,keyboard-interactive:pam' /etc/ssh/sshd_config; then
|
||||
echo "$DISTRO_LIKE" | grep -q -w debian && pamsuffix=debian
|
||||
echo "$DISTRO_LIKE" | grep -q -w rhel && pamsuffix=rhel
|
||||
if [ -n "$pamsuffix" ] && [ -e $ETC_DIR/pam.d/sshd ] && [ -e "$basedir/etc/pam.d/sshd.$pamsuffix" ]; then
|
||||
cp -a "$ETC_DIR/pam.d/sshd" "$ETC_DIR/pam.d/sshd.backup_$(date +%s)"
|
||||
cat "$basedir/etc/pam.d/sshd.$pamsuffix" > $ETC_DIR/pam.d/sshd
|
||||
if grep -Eiq '^[[:space:]]*AuthenticationMethods[[:space:]]+publickey,keyboard-interactive:pam' "$SSH_DIR/sshd_config"; then
|
||||
echo "$DISTRO_LIKE" | grep -q -w debian && pamsuffix=debian
|
||||
echo "$DISTRO_LIKE" | grep -q -w rhel && pamsuffix=rhel
|
||||
[ "$OS_FAMILY" = FreeBSD ] && pamsuffix=freebsd
|
||||
if [ -n "$pamsuffix" ] && [ -e $PAM_SSHD ] && [ -e "$basedir/etc/pam.d/sshd.$pamsuffix" ]; then
|
||||
cp -a "$PAM_SSHD" "$PAM_SSHD.backup_$(date +%s)"
|
||||
cat "$basedir/etc/pam.d/sshd.$pamsuffix" > $PAM_SSHD
|
||||
action_done
|
||||
else
|
||||
action_error "couldn't use our pam.d/sshd template"
|
||||
action_error "couldn't use our pam.d/sshd template (no template for $OS_FAMILY/$DISTRO_LIKE)"
|
||||
fi
|
||||
else
|
||||
action_na "the currently installed sshd_config file doesn't have a forced 'AuthenticationMethods publickey', we can't install our pam.d template safely (it could turn this machine into an allow-all accesses without auth through ssh!)"
|
||||
|
@ -1223,12 +1234,12 @@ fi
|
|||
if [ "${opt[modify-pam-lastlog]}" = 1 ]; then
|
||||
# pam.d lastlogin
|
||||
action_doing "Adjust lastlog in pam.d/sshd if applicable"
|
||||
if [ -e "$ETC_DIR/pam.d/sshd" ] ; then
|
||||
if ! grep -Eq '^\s*session\s+optional\s+pam_lastlog.so' "$ETC_DIR/pam.d/sshd" ; then
|
||||
if [ -e "$PAM_SSHD" ] ; then
|
||||
if ! grep -Eq '^\s*session\s+optional\s+pam_lastlog.so' "$PAM_SSHD" ; then
|
||||
action_detail "missing lastlog config in file, adjusting"
|
||||
# shellcheck disable=SC1004
|
||||
sed_compat '/^\s*@include\s+common-session/a\
|
||||
# bastion config: lastlog needs to be updated on connection\nsession optional pam_lastlog.so silent' "$ETC_DIR/pam.d/sshd"
|
||||
# bastion config: lastlog needs to be updated on connection\nsession optional pam_lastlog.so silent' "$PAM_SSHD"
|
||||
action_done
|
||||
else
|
||||
action_na "lastlog config was already ok"
|
||||
|
|
|
@ -125,8 +125,18 @@ elif echo "$DISTRO_LIKE" | grep -q -w suse; then
|
|||
installed="FIXME"
|
||||
install_cmd="zypper install"
|
||||
elif [ "$OS_FAMILY" = FreeBSD ]; then
|
||||
wanted_list="base64 coreutils rsync bash sudo pamtester p5-JSON p5-JSON-XS p5-common-sense p5-Net-IP p5-GnuPG p5-DBD-SQLite p5-Net-Netmask p5-Term-ReadKey expect fping p5-Net-Server p5-CGI p5-LWP-Protocol-https"
|
||||
install_cmd="pkg add"
|
||||
installed=""
|
||||
for i in $wanted_list
|
||||
do
|
||||
if pkg info -e "$i"; then
|
||||
installed="$installed $i"
|
||||
fi
|
||||
done
|
||||
if [ "$opt_install" = 1 ]; then
|
||||
pkg install -y rsync bash sudo p5-JSON p5-JSON-XS p5-common-sense p5-Net-IP p5-GnuPG p5-DBD-SQLite p5-Net-Netmask p5-Term-ReadKey expect fping p5-Net-Server p5-CGI p5-LWP-Protocol-https
|
||||
# shellcheck disable=SC2086
|
||||
pkg install -y $wanted_list
|
||||
exit $?
|
||||
fi
|
||||
else
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
# KEYSUDOERS # as a gatekeeper, to be able to add the servers to /home/allowkeeper/ACCOUNT/allowed.partial.%GROUP% file
|
||||
# KEYSUDOERS SUPEROWNERS, %%GROUP%-gatekeeper ALL=(allowkeeper) NOPASSWD: /usr/bin/env perl -T %BASEPATH%/bin/helper/osh-accountAddGroupServer --group %GROUP% *
|
||||
# FILEMODE 0750
|
||||
# FILEOWN root allowkeeper
|
||||
# FILEOWN 0 allowkeeper
|
||||
|
||||
#>HEADER
|
||||
use common::sense;
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
# NEEDGROUP osh-accountCreate
|
||||
# SUDOERS %osh-accountCreate ALL=(root) NOPASSWD:/usr/bin/env perl -T /opt/bastion/bin/helper/osh-accountCreate --type normal *
|
||||
# FILEMODE 0700
|
||||
# FILEOWN root root
|
||||
# FILEOWN 0 0
|
||||
|
||||
#>HEADER
|
||||
use common::sense;
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
# NEEDGROUP osh-accountDelete
|
||||
# SUDOERS %osh-accountDelete ALL=(root) NOPASSWD:/usr/bin/env perl -T /opt/bastion/bin/helper/osh-accountDelete *
|
||||
# FILEMODE 0700
|
||||
# FILEOWN root root
|
||||
# FILEOWN 0 0
|
||||
|
||||
#>HEADER
|
||||
use common::sense;
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
# SUDOERS # to be able to generate an egress password for accounts
|
||||
# SUDOERS %osh-accountGeneratePassword ALL=(%bastion-users) NOPASSWD:/usr/bin/env perl -T /opt/bastion/bin/helper/osh-accountGeneratePassword *
|
||||
# FILEMODE 0755
|
||||
# FILEOWN root root
|
||||
# FILEOWN 0 0
|
||||
|
||||
#>HEADER
|
||||
use common::sense;
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
# NEEDGROUP osh-auditor
|
||||
# SUDOERS %osh-auditor ALL=(root) NOPASSWD:/usr/bin/env perl -T /opt/bastion/bin/helper/osh-accountGetPasswordInfo *
|
||||
# FILEMODE 0700
|
||||
# FILEOWN root root
|
||||
# FILEOWN 0 0
|
||||
|
||||
#>HEADER
|
||||
use common::sense;
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
# NEEDGROUP osh-accountListEgressKeys
|
||||
# SUDOERS %osh-accountListEgressKeys ALL=(keyreader) NOPASSWD:/usr/bin/env perl -T /opt/bastion/bin/helper/osh-accountListEgressKeys *
|
||||
# FILEMODE 0750
|
||||
# FILEOWN root keyreader
|
||||
# FILEOWN 0 keyreader
|
||||
|
||||
#>HEADER
|
||||
use common::sense;
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
# NEEDGROUP osh-accountListIngressKeys
|
||||
# SUDOERS %osh-accountListIngressKeys ALL=(keyreader) NOPASSWD:/usr/bin/env perl -T /opt/bastion/bin/helper/osh-accountListIngressKeys *
|
||||
# FILEMODE 0750
|
||||
# FILEOWN root keyreader
|
||||
# FILEOWN 0 keyreader
|
||||
|
||||
#>HEADER
|
||||
use common::sense;
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
# NEEDGROUP osh-accountListPasswords
|
||||
# SUDOERS %osh-accountListPasswords ALL=(%bastion-users) NOPASSWD:/usr/bin/env perl -T /opt/bastion/bin/helper/osh-accountListPasswords *
|
||||
# FILEMODE 0755
|
||||
# FILEOWN root root
|
||||
# FILEOWN 0 0
|
||||
|
||||
#>HEADER
|
||||
use common::sense;
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
# NEEDGROUP osh-accountMFAResetPassword
|
||||
# SUDOERS %osh-accountMFAResetPassword ALL=(root) NOPASSWD:/usr/bin/env perl -T /opt/bastion/bin/helper/osh-accountMFAResetPassword --account *
|
||||
# FILEMODE 0700
|
||||
# FILEOWN root root
|
||||
# FILEOWN 0 0
|
||||
|
||||
#>HEADER
|
||||
use common::sense;
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
# NEEDGROUP osh-accountMFAResetTOTP
|
||||
# SUDOERS %osh-accountMFAResetTOTP ALL=(root) NOPASSWD:/usr/bin/env perl -T /opt/bastion/bin/helper/osh-accountMFAResetTOTP --account *
|
||||
# FILEMODE 0700
|
||||
# FILEOWN root root
|
||||
# FILEOWN 0 0
|
||||
|
||||
#>HEADER
|
||||
use common::sense;
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
# SUDOERS # modify parameters/policy of an account
|
||||
# SUDOERS %osh-accountModify ALL=(root) NOPASSWD:/usr/bin/env perl -T /opt/bastion/bin/helper/osh-accountModify *
|
||||
# FILEMODE 0700
|
||||
# FILEOWN root root
|
||||
# FILEOWN 0 0
|
||||
|
||||
#>HEADER
|
||||
use common::sense;
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
# SUDOERS # revoke access to a command
|
||||
# SUDOERS %osh-accountRevokeCommand ALL=(root) NOPASSWD:/usr/bin/env perl -T /opt/bastion/bin/helper/osh-accountModifyCommand --action revoke *
|
||||
# FILEMODE 0755
|
||||
# FILEOWN root root
|
||||
# FILEOWN 0 0
|
||||
|
||||
#>HEADER
|
||||
use common::sense;
|
||||
|
|
|
@ -3,22 +3,22 @@
|
|||
# NEEDGROUP osh-selfAddPersonalAccess
|
||||
# SUDOERS %osh-selfAddPersonalAccess ALL=(allowkeeper) NOPASSWD:/usr/bin/env perl -T /opt/bastion/bin/helper/osh-accountModifyPersonalAccess --target self --action add *
|
||||
# FILEMODE 0750
|
||||
# FILEOWN root allowkeeper
|
||||
# FILEOWN 0 allowkeeper
|
||||
#
|
||||
# NEEDGROUP osh-accountAddPersonalAccess
|
||||
# SUDOERS %osh-accountAddPersonalAccess ALL=(allowkeeper) NOPASSWD:/usr/bin/env perl -T /opt/bastion/bin/helper/osh-accountModifyPersonalAccess --target any --action add *
|
||||
# FILEMODE 0750
|
||||
# FILEOWN root allowkeeper
|
||||
# FILEOWN 0 allowkeeper
|
||||
#
|
||||
# NEEDGROUP osh-selfDelPersonalAccess
|
||||
# SUDOERS %osh-selfDelPersonalAccess ALL=(allowkeeper) NOPASSWD:/usr/bin/env perl -T /opt/bastion/bin/helper/osh-accountModifyPersonalAccess --target self --action del *
|
||||
# FILEMODE 0750
|
||||
# FILEOWN root allowkeeper
|
||||
# FILEOWN 0 allowkeeper
|
||||
#
|
||||
# NEEDGROUP osh-accountDelPersonalAccess
|
||||
# SUDOERS %osh-accountDelPersonalAccess ALL=(allowkeeper) NOPASSWD:/usr/bin/env perl -T /opt/bastion/bin/helper/osh-accountModifyPersonalAccess --target any --action del *
|
||||
# FILEMODE 0750
|
||||
# FILEOWN root allowkeeper
|
||||
# FILEOWN 0 allowkeeper
|
||||
|
||||
#>HEADER
|
||||
use common::sense;
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
# SUDOERS %osh-accountPIV ALL=(allowkeeper) NOPASSWD:/usr/bin/env perl -T /opt/bastion/bin/helper/osh-accountPIV --step 1 --account *
|
||||
# SUDOERS %osh-accountPIV ALL=(%bastion-users) NOPASSWD:/usr/bin/env perl -T /opt/bastion/bin/helper/osh-accountPIV --step 2 --account *
|
||||
# FILEMODE 0755
|
||||
# FILEOWN root root
|
||||
# FILEOWN 0 0
|
||||
|
||||
#>HEADER
|
||||
use common::sense;
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
# NEEDGROUP osh-accountUnexpire
|
||||
# SUDOERS %osh-accountUnexpire ALL=(%bastion-users) NOPASSWD:/usr/bin/env perl -T /opt/bastion/bin/helper/osh-accountUnexpire *
|
||||
# FILEMODE 0755
|
||||
# FILEOWN root root
|
||||
# FILEOWN 0 0
|
||||
|
||||
#>HEADER
|
||||
use common::sense;
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
# NEEDGROUP osh-admin
|
||||
# SUDOERS %osh-admin ALL=(allowkeeper) NOPASSWD:/usr/bin/env perl -T /opt/bastion/bin/helper/osh-adminMaintenance *
|
||||
# FILEMODE 0750
|
||||
# FILEOWN root allowkeeper
|
||||
# FILEOWN 0 allowkeeper
|
||||
|
||||
#>HEADER
|
||||
use common::sense;
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
# KEYSUDOERS # as an aclkeeper, we can add/del a server from the group server list in /home/%GROUP%/allowed.ip
|
||||
# KEYSUDOERS SUPEROWNERS, %%GROUP%-aclkeeper ALL=(%GROUP%) NOPASSWD: /usr/bin/env perl -T %BASEPATH%/bin/helper/osh-groupAddServer --group %GROUP% *
|
||||
# FILEMODE 0755
|
||||
# FILEOWN root root
|
||||
# FILEOWN 0 0
|
||||
|
||||
#>HEADER
|
||||
use common::sense;
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
# KEYSUDOERS # as a gatekeeper, to be able to symlink in /home/allowkeeper/ACCOUNT the /home/%GROUP%/allowed.ip file
|
||||
# KEYSUDOERS SUPEROWNERS, %%GROUP%-gatekeeper ALL=(allowkeeper) NOPASSWD: /usr/bin/env perl -T %BASEPATH%/bin/helper/osh-groupAddSymlinkToAccount --group %GROUP% *
|
||||
# FILEMODE 0750
|
||||
# FILEOWN root allowkeeper
|
||||
# FILEOWN 0 allowkeeper
|
||||
|
||||
#>HEADER
|
||||
use common::sense;
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
# NEEDGROUP osh-groupCreate
|
||||
# SUDOERS %osh-groupCreate ALL=(root) NOPASSWD:/usr/bin/env perl -T /opt/bastion/bin/helper/osh-groupCreate *
|
||||
# FILEMODE 0700
|
||||
# FILEOWN root root
|
||||
# FILEOWN 0 0
|
||||
|
||||
#>HEADER
|
||||
use common::sense;
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
# NEEDGROUP osh-groupDelete
|
||||
# SUDOERS %osh-groupDelete ALL=(root) NOPASSWD:/usr/bin/env perl -T /opt/bastion/bin/helper/osh-groupDelete *
|
||||
# FILEMODE 0700
|
||||
# FILEOWN root root
|
||||
# FILEOWN 0 0
|
||||
|
||||
#>HEADER
|
||||
use common::sense;
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
# KEYSUDOERS # as an owner, we can generate an egress password for the group
|
||||
# KEYSUDOERS SUPEROWNERS, %%GROUP%-owner ALL=(%GROUP%) NOPASSWD: /usr/bin/env perl -T %BASEPATH%/bin/helper/osh-groupGeneratePassword --group %GROUP% *
|
||||
# FILEMODE 0755
|
||||
# FILEOWN root root
|
||||
# FILEOWN 0 0
|
||||
|
||||
#>HEADER
|
||||
use common::sense;
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
# KEYSUDOERS # as an owner, we can modify the group settings
|
||||
# KEYSUDOERS SUPEROWNERS, %%GROUP%-owner ALL=(%GROUP%) NOPASSWD: /usr/bin/env perl -T %BASEPATH%/bin/helper/osh-groupModify --group %GROUP% *
|
||||
# FILEMODE 0755
|
||||
# FILEOWN root root
|
||||
# FILEOWN 0 0
|
||||
|
||||
#>HEADER
|
||||
use common::sense;
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
# KEYSUDOERS # as a gatekeeper, we can grant/revoke a guest access
|
||||
# KEYSUDOERS SUPEROWNERS, %%GROUP%-gatekeeper ALL=(root) NOPASSWD: /usr/bin/env perl -T %BASEPATH%/bin/helper/osh-groupSetRole --type guest --group %GROUP% *
|
||||
# FILEMODE 0700
|
||||
# FILEOWN root root
|
||||
# FILEOWN 0 0
|
||||
|
||||
#>HEADER
|
||||
use common::sense;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#! /usr/bin/perl -T
|
||||
# vim: set filetype=perl ts=4 sw=4 sts=4 et:
|
||||
# FILEMODE 0700
|
||||
# FILEOWN root root
|
||||
# FILEOWN 0 0
|
||||
|
||||
#>HEADER
|
||||
use common::sense;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#! /usr/bin/perl -T
|
||||
# vim: set filetype=perl ts=4 sw=4 sts=4 et:
|
||||
# FILEMODE 0700
|
||||
# FILEOWN root root
|
||||
# FILEOWN 0 0
|
||||
|
||||
#>HEADER
|
||||
use common::sense;
|
||||
|
|
|
@ -3,5 +3,6 @@
|
|||
"selfMFASetupPassword" , {"pr" : ["<enter>"]}
|
||||
],
|
||||
"master_only": true,
|
||||
"execution_mode_on_freebsd": "system",
|
||||
"terminal_mode": "noecho"
|
||||
}
|
||||
|
|
|
@ -100,7 +100,12 @@ else {
|
|||
}
|
||||
|
||||
# in any case, force this
|
||||
$command[0] = '/usr/bin/ttyrec';
|
||||
if (-e '/usr/local/bin/ttyrec') {
|
||||
$command[0] = '/usr/local/bin/ttyrec';
|
||||
}
|
||||
else {
|
||||
$command[0] = '/usr/bin/ttyrec';
|
||||
}
|
||||
|
||||
# then finally launch the command !
|
||||
my $sysret = system(@command);
|
||||
|
|
|
@ -872,58 +872,32 @@ if ($osh_command) {
|
|||
if ($MFArequiredForPlugin ne 'none' && !$skipMFA) {
|
||||
print "As this is required to run this plugin, entering MFA phase.\n";
|
||||
|
||||
# use system() instead of OVH::Bastion::execute() because we need it to grab the term
|
||||
my $pamtries = 3;
|
||||
while (1) {
|
||||
my $pamsysret = system('pamtester', 'sshd', $sysself, 'authenticate');
|
||||
if ($pamsysret < 0) {
|
||||
main_exit(OVH::Bastion::EXIT_MFA_FAILED, 'mfa_failed', "MFA is required for this plugin, but this bastion is missing the `pamtester' tool, aborting");
|
||||
}
|
||||
elsif ($pamsysret != 0) {
|
||||
if (--$pamtries <= 0) {
|
||||
main_exit(OVH::Bastion::EXIT_MFA_FAILED, 'mfa_failed', "Sorry, but Multi-Factor Authentication failed, aborting");
|
||||
}
|
||||
next;
|
||||
}
|
||||
|
||||
# success, if we are configured to launch a external command on pamtester success, do it.
|
||||
# see the bastion.conf.dist file for usage example.
|
||||
my $MFAPostCommand = OVH::Bastion::config('MFAPostCommand')->value;
|
||||
if (ref $MFAPostCommand eq 'ARRAY' && @$MFAPostCommand) {
|
||||
s/%ACCOUNT%/$self/g for @$MFAPostCommand;
|
||||
$fnret = OVH::Bastion::execute(cmd => $MFAPostCommand, must_succeed => 1);
|
||||
if (!$fnret) {
|
||||
warn_syslog("MFAPostCommand returned a non-zero value: " . $fnret->msg);
|
||||
}
|
||||
}
|
||||
last;
|
||||
}
|
||||
$fnret = OVH::Bastion::do_pamtester(self => $self, sysself => $sysself);
|
||||
$fnret or main_exit(OVH::Bastion::EXIT_MFA_FAILED, 'mfa_failed', $fnret->msg);
|
||||
}
|
||||
|
||||
OVH::Bastion::set_terminal_mode_for_plugin(plugin => $osh_command, action => 'set');
|
||||
if (OVH::Bastion::is_bsd() && $osh_command eq 'selfMFASetupPassword') {
|
||||
system(@cmd);
|
||||
$fnret = R('OK', value => {sysret => $?});
|
||||
}
|
||||
else {
|
||||
# some plugins need to be called with system() instead of ::execute
|
||||
my $is_binary;
|
||||
my $system;
|
||||
|
||||
# get the execution mode required by the plugin
|
||||
my $is_binary;
|
||||
my $system;
|
||||
$fnret = OVH::Bastion::plugin_config(plugin => $osh_command, key => "execution_mode_on_$^O");
|
||||
if (!$fnret || !$fnret->value) {
|
||||
$fnret = OVH::Bastion::plugin_config(plugin => $osh_command, key => "execution_mode");
|
||||
if ($fnret && $fnret->value) {
|
||||
$system = 1 if $fnret->value eq 'system';
|
||||
$is_binary = 1 if $fnret->value eq 'binary';
|
||||
}
|
||||
$ENV{'OSH_IP_FROM'} = $ipfrom; # used in some plugins for is_access_granted()
|
||||
$fnret = OVH::Bastion::execute(
|
||||
cmd => \@cmd,
|
||||
noisy_stdout => 1,
|
||||
noisy_stderr => 1,
|
||||
expects_stdin => 1,
|
||||
system => $system,
|
||||
is_binary => $is_binary,
|
||||
);
|
||||
}
|
||||
if ($fnret && $fnret->value) {
|
||||
$system = 1 if $fnret->value eq 'system';
|
||||
$is_binary = 1 if $fnret->value eq 'binary';
|
||||
}
|
||||
$ENV{'OSH_IP_FROM'} = $ipfrom; # used in some plugins for is_access_granted()
|
||||
$fnret = OVH::Bastion::execute(
|
||||
cmd => \@cmd,
|
||||
noisy_stdout => 1,
|
||||
noisy_stderr => 1,
|
||||
expects_stdin => 1,
|
||||
system => $system,
|
||||
is_binary => $is_binary,
|
||||
);
|
||||
OVH::Bastion::set_terminal_mode_for_plugin(plugin => $osh_command, action => 'restore');
|
||||
|
||||
if (defined $log_insert_id and defined $log_db_name) {
|
||||
|
@ -1306,32 +1280,8 @@ if ($JITMFARequired) {
|
|||
print "... skipping as your account is exempt from MFA\n";
|
||||
}
|
||||
else {
|
||||
# use system() instead of OVH::Bastion::execute() because we need it to grab the term
|
||||
my $pamtries = 3;
|
||||
while (1) {
|
||||
my $pamsysret = system('pamtester', 'sshd', $sysself, 'authenticate');
|
||||
if ($pamsysret < 0) {
|
||||
main_exit(OVH::Bastion::EXIT_MFA_FAILED, 'mfa_failed', "MFA is required for this host, but this bastion is missing the `pamtester' tool, aborting");
|
||||
}
|
||||
elsif ($pamsysret != 0) {
|
||||
if (--$pamtries <= 0) {
|
||||
main_exit(OVH::Bastion::EXIT_MFA_FAILED, 'mfa_failed', "Sorry, but Multi-Factor Authentication failed, I can't connect you to this host");
|
||||
}
|
||||
next;
|
||||
}
|
||||
|
||||
# success, if we are configured to launch a external command on pamtester success, do it.
|
||||
# see the bastion.conf.dist file for usage example.
|
||||
my $MFAPostCommand = OVH::Bastion::config('MFAPostCommand')->value;
|
||||
if (ref $MFAPostCommand eq 'ARRAY' && @$MFAPostCommand) {
|
||||
s/%ACCOUNT%/$self/g for @$MFAPostCommand;
|
||||
$fnret = OVH::Bastion::execute(cmd => $MFAPostCommand, must_succeed => 1);
|
||||
if (!$fnret) {
|
||||
warn_syslog("MFAPostCommand returned a non-zero value: " . $fnret->msg);
|
||||
}
|
||||
}
|
||||
last;
|
||||
}
|
||||
$fnret = OVH::Bastion::do_pamtester(self => $self, sysself => $sysself);
|
||||
$fnret or main_exit(OVH::Bastion::EXIT_MFA_FAILED, 'mfa_failed', $fnret->msg);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ generate_account_sudoers()
|
|||
normalized_account=$(sed -re 's/[^A-Z0-9_]/_/gi' <<< "$account")
|
||||
# as we're reducing the amount of possible chars in normalized_account
|
||||
# we could have collisions: use MD5 to generate a uniq suffix
|
||||
account_suffix=$(md5sum - <<< "$account" | cut -c1-6)
|
||||
account_suffix=$(md5sum_compat - <<< "$account" | cut -c1-6)
|
||||
normalized_account="${normalized_account}_${account_suffix}"
|
||||
# lowercase is prohibited
|
||||
normalized_account=$(tr '[:lower:]' '[:upper:]' <<< "$normalized_account")
|
||||
|
@ -42,8 +42,16 @@ generate_account_sudoers()
|
|||
chmod 0440 "${dst}.tmp"
|
||||
{
|
||||
echo "# generated from install script"
|
||||
for template in $(find "$basedir/etc/sudoers.account.template.d/" -type f | sort)
|
||||
for template in $(find "$basedir/etc/sudoers.account.template.d/" -type f -name "*.sudoers" | sort)
|
||||
do
|
||||
# if $template has two dots, then it's of the form XXX-name.$os.sudoers,
|
||||
# in that case we only include this template if $os is our current OS
|
||||
if [ "$(echo "$template" | cut -d. -f3)" = "sudoers" ]; then
|
||||
if [ "$(echo "$template" | cut -d. -f2 | tr '[:upper:]' '[:lower:]')" != "$(echo "$OS_FAMILY" | tr '[:upper:]' '[:lower:]')" ]; then
|
||||
# not the same OS, skip it
|
||||
continue
|
||||
fi
|
||||
fi
|
||||
echo
|
||||
echo "# $template:"
|
||||
perl -pe "s!%ACCOUNT%!$account!g;s!%NORMACCOUNT%!$normalized_account!g;s!%BASEPATH%!$basedir!g" "$template"
|
||||
|
|
13
etc/pam.d/sshd.freebsd
Normal file
13
etc/pam.d/sshd.freebsd
Normal file
|
@ -0,0 +1,13 @@
|
|||
# PAM configuration for the Secure Shell service
|
||||
|
||||
auth optional pam_echo.so Your account has Multi-Factor Authentication enabled, an additional authentication factor is required (password).
|
||||
auth optional pam_exec.so capture_stdout /opt/bastion/bin/shell/pam_exec_pwd_info.sh
|
||||
auth required pam_unix.so
|
||||
|
||||
account required pam_nologin.so
|
||||
account required pam_login_access.so
|
||||
account required pam_unix.so
|
||||
|
||||
session required pam_permit.so
|
||||
|
||||
password required pam_unix.so no_warn try_first_pass
|
114
etc/ssh/ssh_config.freebsd
Normal file
114
etc/ssh/ssh_config.freebsd
Normal file
|
@ -0,0 +1,114 @@
|
|||
# Hardened SSH bastion config -- modify wisely!
|
||||
# Based on https://wiki.mozilla.org/Security/Guidelines/OpenSSH
|
||||
# With modifications where applicable/needed
|
||||
|
||||
# hardened params follow. every non-needed feature is disabled by default,
|
||||
# following the principle of least rights and least features (more enabled
|
||||
# features mean a more important attack surface).
|
||||
|
||||
# === FEATURES ===
|
||||
|
||||
# disable non-needed sshd features
|
||||
# mitigates CVE-0216-0778
|
||||
UseRoaming no
|
||||
# other unwanted features
|
||||
Tunnel no
|
||||
ForwardAgent no
|
||||
ForwardX11 no
|
||||
GatewayPorts no
|
||||
ControlMaster no
|
||||
|
||||
# === CRYPTOGRAPHY ===
|
||||
|
||||
# enforce the use of ssh version 2 protocol, version 1 is disabled.
|
||||
# all sshd_config options regarding protocol 1 are therefore omitted.
|
||||
Protocol 2
|
||||
|
||||
# list of allowed ciphers.
|
||||
# chacha20-poly1305 is a modern cipher, considered very secure
|
||||
# aes is still the standard, we prefer gcm cipher mode, but also
|
||||
# allow ctr cipher mode for compatibility (ctr is considered secure)
|
||||
# we deny arcfour(rc4), 3des, blowfish and cast
|
||||
# for older remote servers (or esoteric hardware), we might need to add: aes256-cbc,aes192-cbc,aes128-cbc
|
||||
# known gotchas:
|
||||
# - BSD (https://lists.freebsd.org/pipermail/freebsd-bugs/2013-June/053005.html) needs aes256-gcm@openssh.com,aes128-gcm@openssh.com DISABLED
|
||||
# - Old Cisco IOS (such as v12.2) only supports aes128-cbc,3des-cbc,aes192-cbc,aes256-cbc
|
||||
# - Ancient Debians (Sarge) and RedHats (7) only support aes128-cbc,3des-cbc,blowfish-cbc,cast128-cbc,arcfour,aes192-cbc,aes256-cbc,rijndael-cbc@lysator.liu.se,aes128-ctr,aes192-ctr,aes256-ctr
|
||||
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr
|
||||
|
||||
# list of allowed message authentication code algorithms.
|
||||
# etm (encrypt-then-mac) are considered the more secure, we
|
||||
# prefer umac (has been proven secure) then sha2.
|
||||
# for older remote servers, fallback to the non-etm version of
|
||||
# the algorithms. we deny md5 entirely.
|
||||
# for older remote servers (or esoteric hardware), we might need to add: hmac-sha1
|
||||
# Known gotchas:
|
||||
# - Old Cisco IOS (such as v12.2) only supports hmac-sha1,hmac-sha1-96,hmac-md5,hmac-md5-96
|
||||
# - Ancient Debians (Sarge) and RedHats (7) only support hmac-md5,hmac-sha1,hmac-ripemd160,hmac-ripemd160@openssh.com,hmac-sha1-96,hmac-md5-96
|
||||
MACs umac-128-etm@openssh.com,umac-64-etm@openssh.com,hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,umac-128@openssh.com,umac-64@openssh.com,hmac-sha2-512,hmac-sha2-256
|
||||
|
||||
# List of allowed key exchange algorithms.
|
||||
# we prefer curve25519-sha256 which is considered the most modern/secure,
|
||||
# and still allow diffie hellman with group exchange using sha256 which is
|
||||
# the most secure dh-based kex.
|
||||
# we avoid algorithms based on the disputed NIST curves, and anything based
|
||||
# on sha1.
|
||||
# known gotchas:
|
||||
# - Windows needs diffie-hellman-group14-sha1 and also needs to NOT have diffie-hellman-group-exchange-sha1 present in the list AT ALL
|
||||
# - OmniOS 5.11 needs diffie-hellman-group1-sha1
|
||||
# - Old Cisco IOS (such as v12.2) only supports diffie-hellman-group1-sha1
|
||||
# - Ancient Debians (Sarge) and RedHats (7) only support diffie-hellman-group-exchange-sha1,diffie-hellman-group1-sha1
|
||||
KexAlgorithms curve25519-sha256@libssh.org,diffie-hellman-group-exchange-sha256
|
||||
|
||||
# === AUTHENTICATION ===
|
||||
|
||||
# we allow only public key authentication ...
|
||||
PubkeyAuthentication yes
|
||||
# ... not password nor keyboard-interactive
|
||||
# ... (set to yes if sshpass is to be used)
|
||||
PasswordAuthentication no
|
||||
# ChallengeResponseAuthentication=yes forces KbdInteractiveAuthentication=yes in the openssh code!
|
||||
ChallengeResponseAuthentication yes
|
||||
KbdInteractiveAuthentication yes
|
||||
# ... not host-based
|
||||
HostbasedAuthentication no
|
||||
# now we specify the auth methods order we want for manual ssh calls.
|
||||
# NOTE1: as per the ssh source code, an auth method omitted hereafter
|
||||
# will not be used, even if set to "yes" above.
|
||||
# NOTE2: the bastion code (namely, ttyrec), will always set the proper
|
||||
# value explicitly on command-line (pubkey OR sshpass), so the value
|
||||
# specified hereafter will be ignored. if you want to force-disable
|
||||
# a method, set it to "no" in the list above, as those will never be
|
||||
# overridden by the code.
|
||||
PreferredAuthentications publickey,keyboard-interactive
|
||||
|
||||
# === LOGIN ###
|
||||
|
||||
# disable escape character use
|
||||
EscapeChar none
|
||||
|
||||
# detect if a hostkey changed due to DNS spoofing
|
||||
CheckHostIP yes
|
||||
|
||||
# ignore ssh-agent, only use specified keys (-i)
|
||||
IdentitiesOnly yes
|
||||
# disable auto-lookup of ~/.ssh/id_rsa ~/.ssh/id_ecdsa etc.
|
||||
IdentityFile /dev/non/existent/file
|
||||
|
||||
# carry those vars to the other side (includes LC_BASTION)
|
||||
SendEnv LANG LC_*
|
||||
|
||||
# allow usage of SSHFP DNS records
|
||||
VerifyHostKeyDNS ask
|
||||
|
||||
# yell if remote hostkey changed
|
||||
StrictHostKeyChecking ask
|
||||
|
||||
# === SYSTEM ===
|
||||
|
||||
# don't hash the users known_hosts files, in the context of a bastion, this adds no security
|
||||
HashKnownHosts no
|
||||
|
||||
# send an ssh ping each 57 seconds to the client and disconnect after 5 no-replies
|
||||
ServerAliveInterval 57
|
||||
ServerAliveCountMax 5
|
136
etc/ssh/sshd_config.freebsd
Normal file
136
etc/ssh/sshd_config.freebsd
Normal file
|
@ -0,0 +1,136 @@
|
|||
# Hardened SSHD bastion config -- modify wisely!
|
||||
# Based on https://wiki.mozilla.org/Security/Guidelines/OpenSSH
|
||||
# With additional restrictions where applicable
|
||||
|
||||
# -lo and -rt users only have local console login
|
||||
DenyUsers *-rt
|
||||
DenyUsers *-lo
|
||||
|
||||
# hardened params follow. every non-needed feature is disabled by default,
|
||||
# following the principle of least rights and least features (more enabled
|
||||
# features mean a more important attack surface).
|
||||
|
||||
# === FEATURES ===
|
||||
|
||||
# disable non-needed sshd features
|
||||
AllowAgentForwarding no
|
||||
AllowTcpForwarding no
|
||||
AllowStreamLocalForwarding no
|
||||
X11Forwarding no
|
||||
PermitTunnel no
|
||||
PermitUserEnvironment no
|
||||
PermitUserRC no
|
||||
GatewayPorts no
|
||||
|
||||
# === INFORMATION DISCLOSURE ===
|
||||
|
||||
# however, display a legal notice for each connection
|
||||
Banner /etc/ssh/banner
|
||||
|
||||
# don't print the bastion MOTD on connection
|
||||
PrintMotd no
|
||||
|
||||
# === CRYPTOGRAPHY ===
|
||||
|
||||
# enforce the use of ssh version 2 protocol, version 1 is disabled.
|
||||
# all sshd_config options regarding protocol 1 are therefore omitted.
|
||||
Protocol 2
|
||||
|
||||
# only use hostkeys with secure algorithms, and omit the ones using NIST curves
|
||||
HostKey /etc/ssh/ssh_host_ed25519_key
|
||||
HostKey /etc/ssh/ssh_host_rsa_key
|
||||
|
||||
# list of allowed ciphers.
|
||||
# chacha20-poly1305 is a modern cipher, considered very secure
|
||||
# aes is still the standard, we prefer gcm cipher mode, but also
|
||||
# allow ctr cipher mode for compatibility (ctr is still considered secure)
|
||||
# we deny arcfour(rc4), 3des, blowfish and cast
|
||||
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr
|
||||
|
||||
# list of allowed message authentication code algorithms.
|
||||
# etm (encrypt-then-mac) are considered the more secure, we
|
||||
# prefer umac (has been proven secure) then sha2.
|
||||
# for older ssh client, fallback to the non-etm version of
|
||||
# the algorithms.
|
||||
# we deny md5 and sha1
|
||||
MACs umac-128-etm@openssh.com,umac-64-etm@openssh.com,hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,umac-128@openssh.com,umac-64@openssh.com,hmac-sha2-512,hmac-sha2-256
|
||||
|
||||
# List of allowed key exchange algorithms.
|
||||
# we prefer curve25519-sha256 which is considered the most modern/secure,
|
||||
# and still allow diffie hellman with group exchange using sha256 which is
|
||||
# the most secure dh-based kex.
|
||||
# we avoid algorithms based on the disputed NIST curves, and anything based
|
||||
# on sha1.
|
||||
KexAlgorithms curve25519-sha256@libssh.org,diffie-hellman-group-exchange-sha256
|
||||
|
||||
# force rekey every 512M of data or 6 hours of connection, whichever comes first
|
||||
RekeyLimit 512M 6h
|
||||
|
||||
# === AUTHENTICATION ===
|
||||
|
||||
# we allow only public key authentication ...
|
||||
PubkeyAuthentication yes
|
||||
# ... not password
|
||||
PasswordAuthentication no
|
||||
# ... keyboard interactive (needed for MFA through PAM)
|
||||
KbdInteractiveAuthentication yes
|
||||
# ... not kerberos
|
||||
KerberosAuthentication no
|
||||
# ... challenge-response (needed for MFA through PAM)
|
||||
ChallengeResponseAuthentication yes
|
||||
# ... not host-based
|
||||
HostbasedAuthentication no
|
||||
|
||||
# just in case, we also explicitly deny empty passwords
|
||||
PermitEmptyPasswords no
|
||||
|
||||
# this needs to be set at "yes" to allow PAM keyboard-interactive authentication,
|
||||
# which is not a security issue because the AuthenticationMethods below force the use of
|
||||
# either publickey or publickey+keyboard-interactive, hence password-only login is never
|
||||
# possible, for root or any other account for that matter
|
||||
PermitRootLogin yes
|
||||
|
||||
# === LOGIN ===
|
||||
|
||||
# disconnect after 30 seconds if user didn't log in successfully
|
||||
LoginGraceTime 30
|
||||
|
||||
# not more than 1 session per network connection (connection sharing with ssh client's master/shared mode)
|
||||
MaxSessions 1
|
||||
|
||||
# maximum concurrent unauth connections to the sshd daemon
|
||||
MaxStartups 50:30:500
|
||||
|
||||
# accept LANG and LC_* vars (also includes LC_BASTION)
|
||||
AcceptEnv LANG LC_*
|
||||
|
||||
# === SYSTEM ===
|
||||
|
||||
# sshd log level at verbose in auth facility for auditing purposes
|
||||
LogLevel VERBOSE
|
||||
SyslogFacility AUTH
|
||||
|
||||
# check sanity of user HOME dir before allowing user to login
|
||||
StrictModes yes
|
||||
|
||||
# never use dns (slows down connections)
|
||||
UseDNS no
|
||||
|
||||
# use PAM facility
|
||||
UsePAM yes
|
||||
|
||||
# === AuthenticationMethods vs potential root OTP vs potential user MFA ===
|
||||
# 2FA has been configured for root, so we force pubkey+PAM for it
|
||||
#Match User root
|
||||
# AuthenticationMethods publickey,keyboard-interactive:pam
|
||||
# Unconditionally skip PAM auth for members of the bastion-nopam group
|
||||
Match Group bastion-nopam
|
||||
AuthenticationMethods publickey
|
||||
# if in one of the mfa groups, use pam
|
||||
Match Group mfa-totp-configd
|
||||
AuthenticationMethods publickey,keyboard-interactive:pam
|
||||
Match Group mfa-password-configd
|
||||
AuthenticationMethods publickey,keyboard-interactive:pam
|
||||
# by default, always ask the publickey (no PAM)
|
||||
Match All
|
||||
AuthenticationMethods publickey
|
|
@ -0,0 +1,2 @@
|
|||
# under FreeBSD, non-root accounts can't read /etc/spwd.db and there's no helper for pam_unix.so to authenticate users
|
||||
%ACCOUNT% ALL=(root) NOPASSWD:/usr/bin/env pamtester sshd %ACCOUNT% authenticate
|
|
@ -920,4 +920,49 @@ sub build_ttyrec_cmdline {
|
|||
return R('OK', value => {saveFile => $saveFile, cmd => \@ttyrec});
|
||||
}
|
||||
|
||||
sub do_pamtester {
|
||||
my %params = @_;
|
||||
my $sysself = $params{'sysself'};
|
||||
my $self = $params{'self'};
|
||||
my $fnret;
|
||||
|
||||
if (!$sysself || !$self) {
|
||||
return R('ERR_MISSING_PARAMETER', msg => "Missing mandatory arguments 'sysself' or 'self'");
|
||||
}
|
||||
|
||||
# use system() instead of OVH::Bastion::execute() because we need it to grab the term
|
||||
my $pamtries = 3;
|
||||
while (1) {
|
||||
my $pamsysret;
|
||||
if (OVH::Bastion::is_freebsd()) {
|
||||
$pamsysret = system('sudo', '-n', '-u', 'root', '--', '/usr/bin/env', 'pamtester', 'sshd', $sysself, 'authenticate');
|
||||
}
|
||||
else {
|
||||
$pamsysret = system('pamtester', 'sshd', $sysself, 'authenticate');
|
||||
}
|
||||
if ($pamsysret < 0) {
|
||||
return R('KO_MFA_FAILED', msg => "MFA is required for this host, but this bastion is missing the `pamtester' tool, aborting");
|
||||
}
|
||||
elsif ($pamsysret != 0) {
|
||||
if (--$pamtries <= 0) {
|
||||
return R('KO_MFA_FAILED', msg => "Sorry, but Multi-Factor Authentication failed, I can't connect you to this host");
|
||||
}
|
||||
next;
|
||||
}
|
||||
|
||||
# success, if we are configured to launch a external command on pamtester success, do it.
|
||||
# see the bastion.conf.dist file for usage example.
|
||||
my $MFAPostCommand = OVH::Bastion::config('MFAPostCommand')->value;
|
||||
if (ref $MFAPostCommand eq 'ARRAY' && @$MFAPostCommand) {
|
||||
s/%ACCOUNT%/$self/g for @$MFAPostCommand;
|
||||
$fnret = OVH::Bastion::execute(cmd => $MFAPostCommand, must_succeed => 1);
|
||||
if (!$fnret) {
|
||||
warn_syslog("MFAPostCommand returned a non-zero value: " . $fnret->msg);
|
||||
}
|
||||
}
|
||||
last;
|
||||
}
|
||||
return R('OK_MFA_SUCCESS');
|
||||
}
|
||||
|
||||
1;
|
||||
|
|
|
@ -67,6 +67,26 @@ if [ ! -e "$SSH_DIR" ]; then
|
|||
SSH_DIR=/etc/ssh
|
||||
fi
|
||||
|
||||
# set PAM_DIR
|
||||
PAM_DIR=$ETC_DIR/pam.d
|
||||
if [ ! -e "$PAM_DIR" ]; then
|
||||
PAM_DIR=/etc/pam.d
|
||||
fi
|
||||
|
||||
# set PAM_SSHD
|
||||
# under FreeBSD, both /usr/local/etc/pam.d and /etc/pam.d can exist
|
||||
PAM_SSHD="/etc/pam.d/sshd"
|
||||
if [ -e "/usr/local/etc/pam.d/sshd" ]; then
|
||||
# shellcheck disable=SC2034
|
||||
PAM_SSHD="/usr/local/etc/pam.d/sshd"
|
||||
fi
|
||||
|
||||
# set CRON_DIR
|
||||
CRON_DIR=$ETC_DIR/cron.d
|
||||
if [ ! -e "$CRON_DIR" ]; then
|
||||
CRON_DIR=/etc/cron.d
|
||||
fi
|
||||
|
||||
action_doing()
|
||||
{
|
||||
printf '\r*** %b\n' "$*"
|
||||
|
@ -108,6 +128,15 @@ sed_compat()
|
|||
fi
|
||||
}
|
||||
|
||||
md5sum_compat()
|
||||
{
|
||||
if command -v gmd5sum >/dev/null; then
|
||||
gmd5sum "$@"; return $?
|
||||
else
|
||||
md5sum "$@"; return $?
|
||||
fi
|
||||
}
|
||||
|
||||
useradd_compat()
|
||||
{
|
||||
local _user="$1" _uid="" _home="" _shell="" _gid="" _extra=""
|
||||
|
|
|
@ -3,15 +3,18 @@
|
|||
# This entrypoint is ONLY for instances to run functional tests on
|
||||
# DO NOT USE IN PROD (check docker/ under main dir for that)
|
||||
set -e
|
||||
set -u
|
||||
|
||||
basedir=$(readlink -f "$(dirname "$0")"/../../..)
|
||||
# shellcheck source=lib/shell/functions.inc
|
||||
. "$basedir"/lib/shell/functions.inc
|
||||
|
||||
# do we have a key?
|
||||
[ -n "$USER_PUBKEY_B64" ] && user_pubkey=$(base64 -d <<< "$USER_PUBKEY_B64")
|
||||
[ -n "$ROOT_PUBKEY_B64" ] && root_pubkey=$(base64 -d <<< "$ROOT_PUBKEY_B64")
|
||||
if [ -n "$USER_PUBKEY_B64" ]; then
|
||||
user_pubkey=$(base64 -d <<< "$USER_PUBKEY_B64")
|
||||
fi
|
||||
if [ -n "$ROOT_PUBKEY_B64" ]; then
|
||||
root_pubkey=$(base64 -d <<< "$ROOT_PUBKEY_B64")
|
||||
fi
|
||||
if [ -z "$user_pubkey" ] ; then
|
||||
echo "Missing ENV user_pubkey (or USER_PUBKEY_B64), aborting" >&2
|
||||
exit 1
|
||||
|
@ -37,7 +40,9 @@ echo "Port 226" >> /etc/ssh/sshd_config
|
|||
[ -d "$UID0HOME/.ssh" ] || mkdir "$UID0HOME/.ssh"
|
||||
echo "$root_pubkey" >> "$UID0HOME/.ssh/authorized_keys"
|
||||
# also unlock the root account, which can sometimes prevent us connecting through SSH (CentOS 8)
|
||||
usermod -U "$UID0"
|
||||
if [ "$OS_FAMILY" = Linux ]; then
|
||||
usermod -U "$UID0"
|
||||
fi
|
||||
|
||||
HOME="$UID0HOME" USER="$UID0" "$basedir"/bin/plugin/restricted/accountCreate '' '' '' '' --uid 5000 --account "$TARGET_USER" --public-key "$user_pubkey FOR_TESTS_ONLY"
|
||||
HOME="$UID0HOME" USER="$UID0" "$basedir"/bin/plugin/restricted/accountGrantCommand '' '' '' '' --account "$TARGET_USER" --command accountGrantCommand
|
||||
|
@ -51,7 +56,7 @@ cat /home/"$TARGET_USER"/.ssh/id_*.pub > ~test-shell_/.ssh/authorized_keys
|
|||
add_user_to_group_compat test-shell_ bastion-nopam
|
||||
|
||||
# install a fake ttyrec just so that our connection tests work
|
||||
if [ ! -e /usr/bin/ttyrec ] ; then
|
||||
if ! command -v ttyrec >/dev/null; then
|
||||
"$basedir"/bin/admin/install --nothing --no-wait --install-fake-ttyrec
|
||||
fi
|
||||
|
||||
|
@ -70,7 +75,7 @@ fi
|
|||
|
||||
# now OS-specific things
|
||||
|
||||
if [ "$(uname -s)" = Linux ] ; then
|
||||
if [ "$OS_FAMILY" = Linux ] ; then
|
||||
|
||||
test -x /etc/init.d/ssh && /etc/init.d/ssh start
|
||||
test -x /etc/init.d/syslog-ng && /etc/init.d/syslog-ng start
|
||||
|
@ -89,7 +94,7 @@ if [ "$(uname -s)" = Linux ] ; then
|
|||
/usr/sbin/syslog-ng
|
||||
fi
|
||||
|
||||
elif [ "$(uname -s)" = OpenBSD ] || [ "$(uname -s)" = FreeBSD ] || [ "$(uname -s)" = NetBSD ] ; then
|
||||
elif [ "$OS_FAMILY" = OpenBSD ] || [ "$OS_FAMILY" = FreeBSD ] || [ "$OS_FAMILY" = NetBSD ] ; then
|
||||
|
||||
# setup some 127.0.0.x IPs (needed for our tests)
|
||||
# this automatically works under Linux on lo
|
||||
|
@ -110,6 +115,10 @@ elif [ "$(uname -s)" = OpenBSD ] || [ "$(uname -s)" = FreeBSD ] || [ "$(uname -
|
|||
set -e
|
||||
fi
|
||||
|
||||
if [ -n "$NO_SLEEP" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "Now sleeping forever (docker mode)"
|
||||
while : ; do
|
||||
sleep 3600
|
||||
|
|
|
@ -15,16 +15,19 @@ account0="$3"
|
|||
user_ssh_key_path="$4"
|
||||
root_ssh_key_path="$5"
|
||||
osh_etc="$6"
|
||||
remote_basedir="$7"
|
||||
[ -n "$osh_etc" ] || osh_etc=/etc/bastion
|
||||
[ -n "$remote_basedir" ] || remote_basedir="$basedir"
|
||||
|
||||
[ -z "$HAS_ED25519" ] && HAS_ED25519=1
|
||||
[ -z "$HAS_BLACKLIST" ] && HAS_BLACKLIST=0
|
||||
[ -z "$HAS_MFA" ] && HAS_MFA=1
|
||||
[ -z "$HAS_PAMTESTER" ] && HAS_PAMTESTER=1
|
||||
[ -z "$nocc" ] && nocc=0
|
||||
[ -z "$nowait" ] && nowait=0
|
||||
[ -z "$TARGET" ] && TARGET=''
|
||||
[ -z "$TEST_SCRIPT" ] && TEST_SCRIPT=''
|
||||
[ -z "$HAS_ED25519" ] && HAS_ED25519=1
|
||||
[ -z "$HAS_BLACKLIST" ] && HAS_BLACKLIST=0
|
||||
[ -z "$HAS_MFA" ] && HAS_MFA=1
|
||||
[ -z "$HAS_MFA_PASSWORD" ] && HAS_MFA_PASSWORD=0
|
||||
[ -z "$HAS_PAMTESTER" ] && HAS_PAMTESTER=1
|
||||
[ -z "$nocc" ] && nocc=0
|
||||
[ -z "$nowait" ] && nowait=0
|
||||
[ -z "$TARGET" ] && TARGET=''
|
||||
[ -z "$TEST_SCRIPT" ] && TEST_SCRIPT=''
|
||||
|
||||
# die if using an unset var
|
||||
set -u
|
||||
|
@ -103,7 +106,7 @@ cat >"$mytmpdir/ssh_config" <<EOF
|
|||
PasswordAuthentication no
|
||||
RequestTTY yes
|
||||
EOF
|
||||
if [ "$HAS_MFA" = 1 ]; then
|
||||
if [ "$HAS_MFA" = 1 ] || [ "$HAS_MFA_PASSWORD" = 1 ]; then
|
||||
cat >>"$mytmpdir/ssh_config" <<EOF
|
||||
ChallengeResponseAuthentication yes
|
||||
KbdInteractiveAuthentication yes
|
||||
|
@ -230,7 +233,7 @@ script() {
|
|||
return
|
||||
fi
|
||||
|
||||
tmpscript=$(mktemp -p $outdir)
|
||||
tmpscript=$(mktemp)
|
||||
echo "#! /usr/bin/env bash" > "$tmpscript"
|
||||
echo "$*" >> "$tmpscript"
|
||||
chmod 755 "$tmpscript"
|
||||
|
@ -399,7 +402,7 @@ runtests()
|
|||
|
||||
COUNTONLY=0
|
||||
echo === running unit tests ===
|
||||
if ! $r0 perl "$basedir/tests/unit/run.pl"; then
|
||||
if ! $r0 perl "$remote_basedir/tests/unit/run.pl"; then
|
||||
printf "%b%b%b\\n" "$WHITE_ON_RED" "Unit tests failed :(" "$NOC"
|
||||
exit 1
|
||||
fi
|
||||
|
|
|
@ -690,11 +690,11 @@ EOS
|
|||
|
||||
success selfListAccesses a3_list_own_accesses $a3 --osh selfListAccesses
|
||||
json .command selfListAccesses .error_code OK
|
||||
contain REGEX '77\.66\.55\.0/24\s+\(any\)\s+\(any\)\s+personal\s+'$account0'\s'
|
||||
contain REGEX '1\.2\.3\.4\s+\(any\)\s+\(any\)\s+personal\s+'$account0'\s'
|
||||
contain REGEX '77\.66\.55\.4\s+\(any\)\s+\(any\)\s+personal\s+'$account0'\s'
|
||||
contain REGEX '127\.0\.0\.1\s+22\s+g1\s+'$group1'\(group-guest\)\s+'$account2'\s'
|
||||
contain REGEX '10\.20\.0\.0/17\s+\(any\)\s+\(any\)\s+'$group3'\(group-member\)\s+'$account3'\s'
|
||||
contain REGEX '77\.66\.55\.0/24[[:space:]]+\(any\)[[:space:]]+\(any\)[[:space:]]+personal[[:space:]]+'$account0'[[:space:]]'
|
||||
contain REGEX '1\.2\.3\.4[[:space:]]+\(any\)[[:space:]]+\(any\)[[:space:]]+personal[[:space:]]+'$account0'[[:space:]]'
|
||||
contain REGEX '77\.66\.55\.4[[:space:]]+\(any\)[[:space:]]+\(any\)[[:space:]]+personal[[:space:]]+'$account0'[[:space:]]'
|
||||
contain REGEX '127\.0\.0\.1[[:space:]]+22[[:space:]]+g1[[:space:]]+'$group1'\(group-guest\)[[:space:]]+'$account2'[[:space:]]'
|
||||
contain REGEX '10\.20\.0\.0/17[[:space:]]+\(any\)[[:space:]]+\(any\)[[:space:]]+'$group3'\(group-member\)[[:space:]]+'$account3'[[:space:]]'
|
||||
contain "5 accesses listed"
|
||||
|
||||
run accountDelete notingroup $a1 --osh accountDelete --account $account2
|
||||
|
@ -826,11 +826,11 @@ EOS
|
|||
# group1: a1(owner,aclkeeper,gatekeeper,member) a2() servers(127.0.0.10,127.0.0.11,127.0.0.12-TTL)
|
||||
success groupListServers list $a1 --osh groupListServers --group $group1
|
||||
json .command groupListServers .error_code OK
|
||||
contain REGEX '127\.0\.0\.1\s+22\s+g1\s+'$group1'\(group\)\s+'$account2'\s'
|
||||
contain REGEX '127\.0\.0\.2\s+22\s+g2\s+'$group1'\(group\)\s+'$account2'\s'
|
||||
contain REGEX '127\.0\.0\.10\s+\(any\)\s+\(any\)\s+'$group1'\(group\)\s+'$account1'\s'
|
||||
contain REGEX '127\.0\.0\.11\s+\(any\)\s+\(any\)\s+'$group1'\(group\)\s+'$account1'\s'
|
||||
contain REGEX '127\.0\.0\.12\s+\(any\)\s+\(any\)\s+'$group1'\(group\)\s+'$account1'\s+\S+\s+00:00:[01][0123456789]'
|
||||
contain REGEX '127\.0\.0\.1[[:space:]]+22[[:space:]]+g1[[:space:]]+'$group1'\(group\)[[:space:]]+'$account2'[[:space:]]'
|
||||
contain REGEX '127\.0\.0\.2[[:space:]]+22[[:space:]]+g2[[:space:]]+'$group1'\(group\)[[:space:]]+'$account2'[[:space:]]'
|
||||
contain REGEX '127\.0\.0\.10[[:space:]]+\(any\)[[:space:]]+\(any\)[[:space:]]+'$group1'\(group\)[[:space:]]+'$account1'[[:space:]]'
|
||||
contain REGEX '127\.0\.0\.11[[:space:]]+\(any\)[[:space:]]+\(any\)[[:space:]]+'$group1'\(group\)[[:space:]]+'$account1'[[:space:]]'
|
||||
contain REGEX '127\.0\.0\.12[[:space:]]+\(any\)[[:space:]]+\(any\)[[:space:]]+'$group1'\(group\)[[:space:]]+'$account1'[[:space:]]+\S+[[:space:]]+00:00:[01][0123456789]'
|
||||
contain '5 accesses listed'
|
||||
|
||||
# wait for the access to expire
|
||||
|
@ -839,11 +839,11 @@ EOS
|
|||
# group1: a1(owner,aclkeeper,gatekeeper,member) a2() servers(127.0.0.10,127.0.0.11)
|
||||
success groupListServers listttlexpired $a1 --osh groupListServers --group $group1
|
||||
json .command groupListServers .error_code OK
|
||||
contain REGEX '127\.0\.0\.1\s+22\s+g1\s+'$group1'\(group\)\s+'$account2'\s'
|
||||
contain REGEX '127\.0\.0\.2\s+22\s+g2\s+'$group1'\(group\)\s+'$account2'\s'
|
||||
contain REGEX '127\.0\.0\.10\s+\(any\)\s+\(any\)\s+'$group1'\(group\)\s+'$account1'\s'
|
||||
contain REGEX '127\.0\.0\.11\s+\(any\)\s+\(any\)\s+'$group1'\(group\)\s+'$account1'\s'
|
||||
nocontain REGEX '127\.0\.0\.12\s+\(any\)\s+\(any\)\s+'$group1'\(group\)\s+'$account1'\s'
|
||||
contain REGEX '127\.0\.0\.1[[:space:]]+22[[:space:]]+g1[[:space:]]+'$group1'\(group\)[[:space:]]+'$account2'[[:space:]]'
|
||||
contain REGEX '127\.0\.0\.2[[:space:]]+22[[:space:]]+g2[[:space:]]+'$group1'\(group\)[[:space:]]+'$account2'[[:space:]]'
|
||||
contain REGEX '127\.0\.0\.10[[:space:]]+\(any\)[[:space:]]+\(any\)[[:space:]]+'$group1'\(group\)[[:space:]]+'$account1'[[:space:]]'
|
||||
contain REGEX '127\.0\.0\.11[[:space:]]+\(any\)[[:space:]]+\(any\)[[:space:]]+'$group1'\(group\)[[:space:]]+'$account1'[[:space:]]'
|
||||
nocontain REGEX '127\.0\.0\.12[[:space:]]+\(any\)[[:space:]]+\(any\)[[:space:]]+'$group1'\(group\)[[:space:]]+'$account1'[[:space:]]'
|
||||
contain '4 accesses listed'
|
||||
|
||||
# group1: a1(owner,aclkeeper,gatekeeper,member) a2() servers(127.0.0.10,127.0.0.11)
|
||||
|
@ -883,6 +883,9 @@ EOS
|
|||
success groupModify guest_ttl_limit $a1 --osh groupModify --group $group1 --guest-ttl-limit 0
|
||||
json .command groupModify .error_code OK
|
||||
|
||||
# if we're just counting the number of tests, don't sleep
|
||||
[ "$COUNTONLY" != 1 ] && sleep 1
|
||||
|
||||
# group1: a1(owner,aclkeeper,gatekeeper,member) a2() servers(127.0.0.10,127.0.0.11)
|
||||
success groupAddGuestAccess works $a1 --osh groupAddGuestAccess --group $group1 --account $account2 --port-any --user-any --host 127.0.0.10
|
||||
contain "has now access"
|
||||
|
|
|
@ -37,9 +37,9 @@ testsuite_mfa()
|
|||
a4_password=']BkL>3x#T)g~~B#rLv^!T2&N'
|
||||
script mfa a4_setup_pass_step2of2 "echo 'set timeout 30; \
|
||||
spawn $a4 --osh selfMFASetupPassword --yes; \
|
||||
expect \":\" { send \"$a4_password_tmp\\n\"; }; \
|
||||
expect \":\" { send \"$a4_password\\n\"; }; \
|
||||
expect \":\" { send \"$a4_password\\n\"; }; \
|
||||
expect \":\" { sleep 0.2; send \"$a4_password_tmp\\n\"; }; \
|
||||
expect \":\" { sleep 0.2; send \"$a4_password\\n\"; }; \
|
||||
expect \":\" { sleep 0.2; send \"$a4_password\\n\"; }; \
|
||||
expect eof; \
|
||||
lassign [wait] pid spawnid value value; \
|
||||
exit \$value' | expect -f -"
|
||||
|
@ -52,17 +52,17 @@ testsuite_mfa()
|
|||
|
||||
# now try to connect after we have a pass
|
||||
run mfa a4_connect_after_pass $a4f --osh groupList
|
||||
if [ "$HAS_MFA" = 1 ]; then
|
||||
if [ "$HAS_MFA" = 1 ] || [ "$HAS_MFA_PASSWORD" = 1 ]; then
|
||||
# now we need a password, we don't enter it so it'll timeout (124)
|
||||
retvalshouldbe 124
|
||||
contain 'Multi-Factor Authentication enabled, an additional authentication factor is required (password).'
|
||||
contain 'Password:'
|
||||
contain REGEX 'Password:|Password for'
|
||||
nocontain 'JSON_OUTPUT'
|
||||
else
|
||||
# our system doesn't support MFA so it still works without asking for a password
|
||||
retvalshouldbe 0
|
||||
nocontain 'Multi-Factor Authentication enabled'
|
||||
nocontain 'Password:'
|
||||
nocontain REGEX 'Password:|Password for'
|
||||
json .command groupList .error_code OK_EMPTY
|
||||
fi
|
||||
|
||||
|
@ -76,66 +76,71 @@ testsuite_mfa()
|
|||
# setup group to force JIT egress MFA
|
||||
script mfa a4_modify_g3_egress_mfa "echo 'set timeout 30; \
|
||||
spawn $a4 --osh groupModify --group $group3 --mfa-required any; \
|
||||
expect \":\" { send \"$a4_password\\n\"; }; \
|
||||
expect \":\" { 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 'Password:'
|
||||
contain REGEX 'Password:|Password for'
|
||||
json .command groupModify .error_code OK
|
||||
|
||||
# check that the MFA is set for the group
|
||||
script mfa a4_verify_g3_egress_mfa "echo 'set timeout 30; \
|
||||
spawn $a4 --osh groupInfo --group $group3; \
|
||||
expect \":\" { send \"$a4_password\\n\"; }; \
|
||||
expect \":\" { 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 'Password:'
|
||||
contain REGEX 'Password:|Password for'
|
||||
json .command groupInfo .error_code OK
|
||||
json .value.mfa_required any
|
||||
|
||||
# add 127.7.7.7 to this group
|
||||
script mfa a4_add_g3_server "echo 'set timeout 30; \
|
||||
spawn $a4 --osh groupAddServer --group $group3 --host 127.7.7.7 --user-any --port-any --force; \
|
||||
expect \":\" { send \"$a4_password\\n\"; }; \
|
||||
expect \":\" { 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 'Password:'
|
||||
contain REGEX 'Password:|Password for'
|
||||
|
||||
# connect to 127.7.7.7 with MFA JIT, bad password
|
||||
script mfa a4_connect_g3_server_badpass "echo 'set timeout 45; \
|
||||
spawn $a4 root@127.7.7.7; \
|
||||
expect \"word:\" { send \"$a4_password\\n\"; }; \
|
||||
expect \"word:\" { send \"BADPASSWORD\\n\"; }; \
|
||||
expect \"word:\" { send \"BADPASSWORD\\n\"; }; \
|
||||
expect \"word:\" { send \"BADPASSWORD\\n\\n\"; }; \
|
||||
expect \"is required (password)\" { sleep 0.1; }; \
|
||||
expect \":\" { sleep 0.2; send \"$a4_password\\n\"; }; \
|
||||
expect \"is required (password)\" { sleep 0.1; }; \
|
||||
expect \":\" { sleep 0.2; send \"BADPASSWORD\\n\"; }; \
|
||||
expect \"is required (password)\" { sleep 0.1; }; \
|
||||
expect \":\" { sleep 0.2; send \"BADPASSWORD\\n\"; }; \
|
||||
expect \"is required (password)\" { sleep 0.1; }; \
|
||||
expect \":\" { sleep 0.2; send \"BADPASSWORD\\n\\n\"; }; \
|
||||
expect eof; \
|
||||
lassign [wait] pid spawnid value value; \
|
||||
exit \$value' | expect -f -"
|
||||
retvalshouldbe 125
|
||||
contain 'Multi-Factor Authentication enabled, an additional authentication factor is required (password).'
|
||||
contain 'Password:'
|
||||
contain REGEX 'Password:|Password for'
|
||||
contain 'pamtester: '
|
||||
nocontain 'Permission denied'
|
||||
|
||||
# connect to 127.7.7.7 with MFA JIT, good password
|
||||
script mfa a4_connect_g3_server_goodpass "echo 'set timeout 30; \
|
||||
spawn $a4 root@127.7.7.7; \
|
||||
expect \"word:\" { send \"$a4_password\\n\"; }; \
|
||||
expect \"word:\" { send \"$a4_password\\n\"; }; \
|
||||
expect \":\" { sleep 0.2; send \"$a4_password\\n\"; }; \
|
||||
expect \"is required (password)\" { sleep 0.1; }; \
|
||||
expect \":\" { sleep 0.2; send \"$a4_password\\n\"; }; \
|
||||
expect eof; \
|
||||
lassign [wait] pid spawnid value value; \
|
||||
exit \$value' | expect -f -"
|
||||
retvalshouldbe 255
|
||||
contain 'Multi-Factor Authentication enabled, an additional authentication factor is required (password).'
|
||||
contain 'Password:'
|
||||
contain REGEX 'Password:|Password for'
|
||||
contain 'pamtester: successfully authenticated'
|
||||
contain 'Permission denied'
|
||||
|
||||
|
@ -150,7 +155,7 @@ testsuite_mfa()
|
|||
# add to JIT MFA group
|
||||
script mfa a0_add_a3_as_member "echo 'set timeout 30; \
|
||||
spawn $a4 --osh groupAddMember --group $group3 --account $account3; \
|
||||
expect \"word:\" { send \"$a4_password\\n\"; }; \
|
||||
expect \":\" { sleep 0.2; send \"$a4_password\\n\"; }; \
|
||||
expect eof; \
|
||||
lassign [wait] pid spawnid value value; \
|
||||
exit \$value' | expect -f -"
|
||||
|
@ -187,25 +192,25 @@ testsuite_mfa()
|
|||
|
||||
# change our password
|
||||
a4_password_new="rkw=*Ffyqs23"
|
||||
if [ "$HAS_MFA" = 1 ]; then
|
||||
if [ "$HAS_MFA" = 1 ] || [ "$HAS_MFA_PASSWORD" = 1 ]; then
|
||||
script mfa a4_change_pass "echo 'set timeout 30; \
|
||||
spawn $a4 --osh selfMFASetupPassword --yes; \
|
||||
expect \":\" { send \"$a4_password\\n\"; }; \
|
||||
expect \":\" { send \"$a4_password\\n\"; }; \
|
||||
expect \":\" { send \"$a4_password_new\\n\"; }; \
|
||||
expect \":\" { send \"$a4_password_new\\n\"; }; \
|
||||
expect \":\" { sleep 0.2; send \"$a4_password\\n\"; }; \
|
||||
expect \":\" { sleep 0.2; send \"$a4_password\\n\"; }; \
|
||||
expect \":\" { sleep 0.2; send \"$a4_password_new\\n\"; }; \
|
||||
expect \":\" { sleep 0.2; send \"$a4_password_new\\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 'Password:'
|
||||
contain REGEX 'Password:|Password for'
|
||||
else
|
||||
script mfa a4_change_pass "echo 'set timeout 30; \
|
||||
spawn $a4 --osh selfMFASetupPassword --yes; \
|
||||
expect \":\" { send \"$a4_password\\n\"; }; \
|
||||
expect \":\" { send \"$a4_password_new\\n\"; }; \
|
||||
expect \":\" { send \"$a4_password_new\\n\"; }; \
|
||||
expect \":\" { sleep 0.2; send \"$a4_password\\n\"; }; \
|
||||
expect \":\" { sleep 0.2; send \"$a4_password_new\\n\"; }; \
|
||||
expect \":\" { sleep 0.2; send \"$a4_password_new\\n\"; }; \
|
||||
expect eof; \
|
||||
lassign [wait] pid spawnid value value; \
|
||||
exit \$value' | expect -f -"
|
||||
|
@ -219,16 +224,16 @@ testsuite_mfa()
|
|||
a4_password="$a4_password_new"
|
||||
unset a4_password_new
|
||||
|
||||
if [ "$HAS_MFA" = 1 ]; then
|
||||
if [ "$HAS_MFA" = 1 ] || [ "$HAS_MFA_PASSWORD" = 1 ]; then
|
||||
script mfa a4_connect_with_pass "echo 'set timeout 30; \
|
||||
spawn $a4 --osh groupList; \
|
||||
expect \":\" { send \"$a4_password\\n\"; }; \
|
||||
expect \":\" { 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 'Password:'
|
||||
contain REGEX 'Password:|Password for'
|
||||
json .command groupList .error_code OK_EMPTY
|
||||
fi
|
||||
|
||||
|
@ -241,10 +246,10 @@ testsuite_mfa()
|
|||
json .error_code OK .command accountModify .value.mfa_totp_required.error_code OK_NO_CHANGE
|
||||
|
||||
# now try to connect with account4
|
||||
if [ "$HAS_MFA" = 1 ]; then
|
||||
if [ "$HAS_MFA" = 1 ] || [ "$HAS_MFA_PASSWORD" = 1 ]; then
|
||||
script mfa a4_connect_with_totpreq "echo 'set timeout 30; \
|
||||
spawn $a4 --osh groupList; \
|
||||
expect \":\" { send \"$a4_password\\n\"; }; \
|
||||
expect \":\" { sleep 0.2; send \"$a4_password\\n\"; }; \
|
||||
expect eof; \
|
||||
lassign [wait] pid spawnid value value; \
|
||||
exit \$value' | expect -f -"
|
||||
|
@ -258,14 +263,14 @@ testsuite_mfa()
|
|||
# setup totp
|
||||
script mfa a4_setup_totp "echo 'set timeout 30; \
|
||||
spawn $a4 --osh selfMFASetupTOTP --no-confirm; \
|
||||
expect \"word:\" { send \"$a4_password\\n\"; }; \
|
||||
expect \"word:\" { send \"$a4_password\\n\"; }; \
|
||||
expect \"word:\" { sleep 0.2; send \"$a4_password\\n\"; }; \
|
||||
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 'Password:'
|
||||
contain REGEX 'Password:|Password for'
|
||||
|
||||
a4_totp_code_1=$(get_stdout | grep -A1 'Your emergency scratch codes are:' | tail -n1 | tr -d '[:space:]')
|
||||
#a4_totp_code_2=$(get_stdout | grep -A2 'Your emergency scratch codes are:' | tail -n1 | tr -d '[:space:]')
|
||||
|
@ -275,7 +280,7 @@ testsuite_mfa()
|
|||
# login and fail without totp (timeout)
|
||||
script mfa a4_connect_after_totp_fail "echo 'set timeout 30; \
|
||||
spawn $a4 --osh groupList; \
|
||||
expect \"word:\" { send \"$a4_password\\n\"; }; \
|
||||
expect \"word:\" { sleep 0.2; send \"$a4_password\\n\"; }; \
|
||||
expect eof; \
|
||||
lassign [wait] pid spawnid value value; \
|
||||
exit \$value' | expect -f -"
|
||||
|
@ -284,30 +289,30 @@ testsuite_mfa()
|
|||
contain 'Multi-Factor Authentication enabled, an additional authentication factor is required (OTP).'
|
||||
contain 'Your password expires on'
|
||||
contain 'in 14 days'
|
||||
contain 'Password:'
|
||||
contain REGEX 'Password:|Password for'
|
||||
contain 'Verification code:'
|
||||
nocontain 'JSON_OUTPUT'
|
||||
|
||||
# success with password + totp
|
||||
script mfa a4_connect_after_totp_ok "echo 'set timeout 30; \
|
||||
spawn $a4 --osh groupList; \
|
||||
expect \"word:\" { send \"$a4_password\\n\"; }; \
|
||||
expect \"code:\" { send \"$a4_totp_code_1\\n\"; }; \
|
||||
expect \"word:\" { sleep 0.2; send \"$a4_password\\n\"; }; \
|
||||
expect \"code:\" { sleep 0.2; send \"$a4_totp_code_1\\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 'Multi-Factor Authentication enabled, an additional authentication factor is required (OTP).'
|
||||
contain 'Password:'
|
||||
contain REGEX 'Password:|Password for'
|
||||
contain 'Verification code:'
|
||||
json .command groupList .error_code OK_EMPTY
|
||||
|
||||
# totp scratch codes don't work twice
|
||||
script mfa a4_connect_after_totp_dupe "echo 'set timeout 30; \
|
||||
spawn $a4 --osh groupList; \
|
||||
expect \"word:\" { send \"$a4_password\\n\"; }; \
|
||||
expect \"code:\" { send \"$a4_totp_code_1\\n\"; }; \
|
||||
expect \"word:\" { sleep 0.2; send \"$a4_password\\n\"; }; \
|
||||
expect \"code:\" { sleep 0.2; send \"$a4_totp_code_1\\n\"; }; \
|
||||
expect \"word:\" { exit 222; }; \
|
||||
expect eof; \
|
||||
lassign [wait] pid spawnid value value; \
|
||||
|
@ -315,7 +320,7 @@ testsuite_mfa()
|
|||
retvalshouldbe 222
|
||||
contain 'Multi-Factor Authentication enabled, an additional authentication factor is required (password).'
|
||||
contain 'Multi-Factor Authentication enabled, an additional authentication factor is required (OTP).'
|
||||
contain 'Password:'
|
||||
contain REGEX 'Password:|Password for'
|
||||
contain 'Verification code:'
|
||||
nocontain 'JSON_OUTPUT'
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue