the-bastion/bin/admin/install
2024-02-20 17:41:53 +01:00

1306 lines
51 KiB
Bash
Executable file

#! /usr/bin/env bash
# vim: set filetype=sh ts=4 sw=4 sts=4 et:
basedir=$(readlink -f "$(dirname "$0")"/../..)
# shellcheck source=lib/shell/functions.inc
. "$basedir"/lib/shell/functions.inc
# instead of setting +e, count the errors we might have and report at the end,
# as it's better to go on instead of abrutly halting in the middle of the install
# for a potentially minor hiccup
trap_err_nb=0
trap_err_lines=''
script_end_reached=0
trap '(( ++trap_err_nb )); trap_err_lines="$trap_err_lines $LINENO"' ERR
trap_exit() {
if [ "$script_end_reached" = 0 ]; then
action_doing "Installation or upgrade interrupted!"
action_error "... as a result, the software might not be in a usable state!"
action_error "... Please run this script again to complete installation or upgrade!"
else
action_doing "Installation or upgrade done, ensuring no error occurred"
if [ "$trap_err_nb" != 0 ]; then
action_error "Some errors have occurred above, please review and advise!"
for line in $trap_err_lines; do
action_detail "... error on script source line $line"
done
exit 1
else
action_done
fi
fi
}
trap 'trap_exit' EXIT
declare -A opt
TTYREC_VERSION_NEEDED=1.1.7.0
set_default_options()
{
opt[modify-banner]=0
opt[modify-sshd-config]=0
opt[modify-ssh-config]=0
opt[modify-motd]=0
opt[modify-umask]=0
opt[modify-pam-sshd]=0
opt[modify-pam-lastlog]=0
opt[remove-weak-moduli]=0
opt[regen-hostkeys]=0
opt[logrotate]=1
opt[overwrite-logrotate]=1
opt[generate-mfa-secret]=1
# special case:
# If $autodetect_startup_system is 1, we'll autodetect whether we will install the
# systemd units (preferred) or the init.d files.
# Any specification of a --[no-]init or --[no]systemd-units option by the user inhibits
# this behavior. So even if both options below default to 0, one of those will in effect
# be set to 1 if $autodetect_startup_system is 1
autodetect_startup_system=1
opt[init]=0
opt[systemd-units]=0
# /special
# special case 2:
# If $autodetect_selinux is 1, we'll autodetect whether we're running on a system
# supporting SELinux, and if yes, we'll proceed in deploying the module to make it
# possible to use TOTP MFA on those systems.
# Any specification of --[no-]install-selinux-module will inhibit this behavior
autodetect_selinux=1
opt[install-selinux-module]=0
# /special case 2
opt[profile]=1
opt[cron]=1
opt[overwrite-cron]=1
opt[syslog-ng]=1
opt[overwrite-syslog-ng]=1
opt[check-ttyrec]=1
opt[install-fake-ttyrec]=0
}
set_default_options
show_help=0
[ -z "$1" ] && show_help=1
nothing=0
while [ -n "$1" ]; do
if [ "$1" = "--new-install" ]; then
set_default_options
opt[modify-banner]=1
opt[modify-sshd-config]=1
opt[modify-ssh-config]=1
opt[modify-motd]=1
opt[modify-umask]=1
opt[modify-pam-sshd]=1
opt[modify-pam-lastlog]=1
opt[remove-weak-moduli]=1
opt[regen-hostkeys]=1
elif [ "$1" = "--minimal" ] || [ "$1" = "--nothing" ]; then
set_default_options
opt[logrotate]=0
opt[overwrite-logrotate]=0
autodetect_startup_system=0
autodetect_selinux=0
opt[profile]=0
opt[cron]=0
opt[overwrite-cron]=0
opt[syslog-ng]=0
opt[overwrite-syslog-ng]=0
[ "$1" = "--nothing" ] && nothing=1
elif [ "$1" = "--managed-upgrade" ] || [ "$1" = "--puppet" ]; then
set_default_options
opt[logrotate]=1
opt[overwrite-logrotate]=0
autodetect_startup_system=1
autodetect_selinux=1
opt[profile]=0
opt[cron]=1
opt[overwrite-cron]=1
opt[syslog-ng]=0
opt[overwrite-syslog-ng]=0
elif [ "$1" = "--upgrade" ]; then
set_default_options
else
# "--[no]-wait" is no longer used, but to keep compatibility, we keep it here (ignored)
# same for --migration-grant-aclkeeper-to-gatekeepers
foundoption=0
for allowedopt in modify-banner modify-sshd-config modify-ssh-config modify-motd modify-umask \
modify-pam-lastlog remove-weak-moduli regen-hostkeys overwrite-logrotate overwrite-cron \
overwrite-syslog-ng logrotate cron syslog-ng migration-grant-aclkeeper-to-gatekeepers \
init systemd-units profile modify-pam-sshd wait check-ttyrec install-fake-ttyrec \
install-selinux-module generate-mfa-secret
do
if [ "$1" = "--no-$allowedopt" ]; then
opt[$allowedopt]=0
foundoption=1
elif [ "$1" = "--$allowedopt" ]; then
opt[$allowedopt]=1
foundoption=1
fi
if [ "$1" = "init" ] || [ "$1" = "systemd-units" ]; then
# see "special case" comment above for more information
autodetect_startup_system=0
fi
if [ "$1" = "install-selinux-module" ]; then
# see "special case 2" comment above for more information
autodetect_selinux=0
fi
done
if [ "$foundoption" != 1 ]; then
echo "$0: Unrecognized option '$1'"
show_help=1
fi
fi
shift
done
if [ "$show_help" = 1 ] ; then
cat <<EOF
This script installs (or upgrades) the necessary files and settings on the system for a bastion server
Usage:
You must specify one of the 5 modes below. Then possibly zero or more options:
MODES:
$0 --new-install
Will modify ssh/sshd, pam, default umask configs under $ETC_DIR/ and regenerate host keys.
Use this if you're installing a new bastion. This is the equivalent of specifying all
the options below that are tagged [N].
Mostly useful for new installations, avoid using if you already have users.
If your bastion system configuration is partly handled by another system such as Puppet/Chef/Ansible,
you might want to specify which parts you want this script to handle and which others to
avoid touching, by using --upgrade instead and eventually some of the options below. You
can also use --new-install but override some options with their --no-* form.
$0 --upgrade
Will avoid touching most non-bastion configuration under $ETC_DIR/, mainly useful when applying
a new code release, to ensure the potential modifications to bastion configuration is done
without touching the other parts of the system. This is the equivalent of specifying all
the options below that are tagged [U]. Recommended in most cases.
$0 --managed-upgrade
Use this when you're using some kind of software configuration manager such as Puppet/Chef/Ansible
to manage most of the system-related configuration of your bastion. This is a light --upgrade mode,
and the equivalent of specifying all the options below that are tagged [M].
These might change in the future.
$0 --minimal
Use when you want to prevent this script from touching anything not directly bastion-related.
This is the equivalent of disabling all the below options (you can then enable them one by one
where needed). You can see it as a yet lighter --upgrade mode. Useful if you want to handle
yourself satellite configuration files such as logrotate or cron.
$0 --nothing
Don't do anything at all, not even what we normally do when all below options are disabled (as
in the minimal mode). Can be useful if you want to do only one precise modification that you
can explicitly specify using the options below.
OPTIONS:
--[no-]modify-banner install a default sshd banner [N]
--[no-]modify-sshd-config apply a hardened ssh server config from our template [N]
--[no-]modify-ssh-config apply a hardened ssh client config from our template [N]
--[no-]modify-motd empty the motd file so that users don't get nagged [N]
--[no-]modify-umask enforce a default umask of 077 on the system [N]
--[no-]modify-pam-sshd overwrite current system PAM configuration for sshd with our template [N]
--[no-]modify-pam-lastlog modify PAM configuration to write lastlog for bastion users [N]
--[no-]init copy satellite daemon scripts for startup in system init.d/ [N, M]
--[no-]systemd-units copy satellite daemon systemd unit files for startup [N, M]
--[no-]profile apply our hardened configuration in system profile.d/ [N]
--[no-]remove-weak-moduli remove weak (<4096) moduli from the ssh server moduli file [N]
--[no-]regen-hostkeys generate new host keys (and trash the old ones) for ssh server [N]
--[no-]generate-mfa-secret generate a new MFA token secret, will never overwrite a preexisting one [N, U]
--[no-]logrotate put our logrotate config files in system logrotate.d/ [N, U, M]
--[no-]overwrite-logrotate overwrite possibly existing files in system logrotate.d/ with our templates [N, U]
--[no-]cron put our cron config files in system cron.d/ [N, U, M]
--[no-]overwrite-cron overwrite possibly existing files in system cron.d/ with our templates [N, U]
--[no-]syslog-ng put our syslog-ng config files in syslog-ng.d/ [N, U]
--[no-]overwrite-syslog-ng overwrite possibly existing files in system syslog-ng.d/ with our templates [N, U]
--[no-]check-ttyrec verify that the ttyrec installed version is compatible with our code [N, U, M]
--[no-]install-fake-ttyrec install a fake ttyrec binary if ttyrec is not present; useful mainly for tests,
or if you *really* don't want to use the real ttyrec
EOF
exit 1
fi
# if we're under FreeBSD, we must have ACLs enabled on /home (or whatever partition contains it),
# so check for that first
if [ "$OS_FAMILY" = FreeBSD ]; then
action_doing "Running under FreeBSD, verifying if /home is mounted with ACLs..."
home_mp="$(df /home 2>/dev/null | awk '{ if(NR==2) {print $6;exit} }')"
if [ -z "$home_mp" ]; then
# no /home? nevermind, / will probably do
home_mp=/
fi
if ! mount | awk '{ if($3=="'"$home_mp"'"){print;exit} }' | grep -q -w acls; then
if mount | awk '{ if($3=="'"$home_mp"'"){print;exit} }' | grep -q -w zfs; then
action_error "No. This is a ZFS mount, which might not be POSIX acls compliant (nfsv4acls supported only)."
action_error "You can still try to enable POSIX acls by running \`mount -u -o acls $home_mp' to apply the change on-the-fly."
else
action_error "No. Please modify your /etc/fstab accordingly, and run \`mount -u -o acls $home_mp' to apply the change on-the-fly."
fi
exit 1
else
action_done
fi
fi
if [ "${opt[install-fake-ttyrec]}" = 1 ]; then
action_doing "Installing fake ttyrec (use this only for tests!)"
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
fi
fi
if [ "${opt[modify-ssh-config]}" = 1 ] || [ "${opt[modify-sshd-config]}" = 1 ] ; then
action_doing "Find which ssh/sshd config templates to install on $OS_FAMILY $LINUX_DISTRO $DISTRO_VERSION"
short_suffix_name=$(echo "$LINUX_DISTRO$DISTRO_VERSION_MAJOR" | sed -re "s/[^a-z0-9]//")
[ "$short_suffix_name" = "rocky8" ] && short_suffix_name=centos8
[ "$short_suffix_name" = "rocky9" ] && short_suffix_name=centos9
filesuffix=default
if [ -e "$basedir/etc/ssh/sshd_config.$short_suffix_name" ] && [ -e "$basedir/etc/ssh/ssh_config.$short_suffix_name" ]; then
filesuffix=$short_suffix_name
elif [ "$OS_FAMILY" = Linux ]; then
if [ "$LINUX_DISTRO" = ubuntu ]; then
if [ "$DISTRO_VERSION_MAJOR" -le 16 ]; then
filesuffix=debian9
elif [ "$DISTRO_VERSION_MAJOR" -le 18 ]; then
filesuffix=debian10
elif [ "$DISTRO_VERSION_MAJOR" -le 20 ]; then
filesuffix=debian11
else
filesuffix=debian12
fi
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"
if [ "${opt[modify-ssh-config]}" = 1 ] ; then
action_doing "Install hardened configuration for ssh to $SSH_DIR/ssh_config"
install -o "$UID0" -g "$GID0" -m 0644 "$basedir/etc/ssh/ssh_config.$filesuffix" $SSH_DIR/ssh_config
fi
if [ "${opt[modify-sshd-config]}" = 1 ] ; then
action_doing "Install hardened configuration for sshd to $SSH_DIR/sshd_config"
install -o "$UID0" -g "$GID0" -m 0644 "$basedir/etc/ssh/sshd_config.$filesuffix" $SSH_DIR/sshd_config
fi
action_done
fi
if [ "${opt[modify-banner]}" = 1 ] ; then
action_doing "Install default sshd banner"
install -o "$UID0" -g "$GID0" -m 0644 "$basedir/etc/ssh/banner" $SSH_DIR/
action_done
fi
if [ "${opt[modify-motd]}" = 1 ] ; then
action_doing "Empty the /etc/motd file"
if [ -f /etc/motd ] && [ ! -s /etc/motd ] ; then
mv /etc/motd /etc/motd.bastion-backup
cat /dev/null > /etc/motd
chmod 644 /etc/motd
action_done
else
action_na "File already empty or non existing"
fi
fi
if [ "${opt[regen-hostkeys]}" = 1 ] ; then
action_doing "Change sshd host keys (this can take a while)"
rm -f $SSH_DIR/ssh_host_{dsa,rsa,ecdsa,ed25519}_key{,.pub}
ssh-keygen -q -t rsa -b 4096 -N '' -f $SSH_DIR/ssh_host_rsa_key >/dev/null
ssh-keygen -q -t ecdsa -b 521 -N '' -f $SSH_DIR/ssh_host_ecdsa_key >/dev/null || true
ssh-keygen -q -A >/dev/null || true
action_done
action_doing "Restart sshd"
ret=-1
if [ -e /etc/rc.d/sshd ] ; then
/etc/rc.d/sshd restart; ret=$?
elif [ -e /etc/init.d/ssh ] ; then
/etc/init.d/ssh restart; ret=$?
fi
if [ $ret -eq 0 ]; then
action_done
else
action_error "You might want to check if the sshd config is valid!"
fi
fi
if [ "$nothing" = 0 ]; then
action_doing "Add .ssh in /etc/skel if needed"
if [ ! -d /etc/skel/.ssh ] && [ -d /etc/skel ]; then
if mkdir /etc/skel/.ssh; then
action_done
else
action_error "Couldn't create /etc/skel/.ssh, nevermind, proceeding"
fi
else
action_na
fi
action_doing "Create $BASTION_ETC_DIR if needed"
if [ -d $BASTION_ETC_DIR ]; then
action_na
else
mkdir $BASTION_ETC_DIR
action_done
fi
chmod 751 $BASTION_ETC_DIR
# to make the otp pam module of our default config happy
action_doing "Create /var/otp if needed"
if [ ! -d /var/otp ]; then
if mkdir /var/otp; then
action_done
else
action_error
fi
else
action_na
fi
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 $destdir..."
[ -d "$destdir" ] || continue
for file in "$basedir/etc/$subdir"/*.dist ; do
destfile="$destdir/$(basename "$file" .dist)"
if [ -e "$destfile" ]; then
# if the target already exist, check if we're asked to overwrite it
if [ "$todo" = "logrotate" ] && [ "${opt[overwrite-logrotate]}" = 1 ]; then
: # we'll overwrite
elif [ "$todo" = "cron" ] && [ "${opt[overwrite-cron]}" = 1 ]; then
: # we'll overwrite
elif [ "$todo" = "syslog" ] && [ "${opt[overwrite-syslog-ng]}" = 1 ]; then
: # we'll overwrite
else
# in all other cases, don't overwrite
action_detail "... won't overwrite $destfile"
continue
fi
action_detail "... we will overwrite $destfile"
elif [ "$(basename "$file")" = "osh-encrypt-rsync.conf.dist" ] || \
[ "$(basename "$file")" = "osh-backup-acl-keys.conf.dist" ] || \
[ "$(basename "$file")" = "osh-remove-empty-folders.conf.dist" ]; then
# special case for those files: if we have the $file.d dir available, don't do anything
if [ -d "$destfile".d ]; then
action_detail "... won't copy file to $destfile as we have $destfile.d"
continue
fi
fi
# if the dest file didn't exist, or it does but we've been asked to overwrite it, do it
# copy our template .dist to proper location in system
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...
[ "$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)
if grep -qE '%RANDOM[1-9]%[0-9]+:[0-9]+%' "$destfile"; then
for i in $(seq 1 9)
do
placeholder=$(grep -Eo "%RANDOM$i%[0-9]+:[0-9]+%" "$destfile" | head -n1)
[ -n "$placeholder" ] || continue
# we have a match, compute the random and do the replace
n=$(echo "$placeholder" | cut -d% -f3 | cut -d: -f1)
m=$(echo "$placeholder" | cut -d% -f3 | cut -d: -f2)
if [ $(( m-n )) -eq 0 ]; then
action_detail "... we would divide by zero! fallback to a non-random random, such as $n"
random=$n
else
# 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"
done
fi
done
done
for base in osh-encrypt-rsync.conf osh-backup-acl-keys.conf osh-remove-empty-folders.conf; do
if [ -f "$BASTION_ETC_DIR/$base" ]; then
chmod 0600 "$BASTION_ETC_DIR/$base"
fi
if [ -d "$BASTION_ETC_DIR/$base.d" ]; then
chmod 0700 "$BASTION_ETC_DIR/$base.d"
fi
done
# remove deprecated cron files
if [ "${opt[cron]}" = 1 ]; then
rm -f "$CRON_DIR/osh-compress-old-logs"
fi
# ensure the includedir is present in global sudoers conf
action_doing "Add include directory in sudoers if needed"
if [ ! -e $SUDOERS_FILE ] ; then
action_error "$SUDOERS_DIR doesn't exist, is sudo installed?"
else
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
echo "#includedir $SUDOERS_DIR" >> $SUDOERS_FILE
action_done
fi
fi
action_doing "Create sudoers.d if needed"
if [ ! -d "$SUDOERS_DIR" ]; then
mkdir -p "$SUDOERS_DIR"
action_done
else
action_na
fi
# List all sudoers.d files matching osh-*. Then, each time we regenerate one below,
# we'll delete it from the list. This way, when we're done, and if any file name remains,
# it means we should delete it because it's obsolete. This way of upgrading in place
# tries to avoid a race condition in sudo where it stat()s all the sudoers.d files, then
# opens them one by one, if some files have disappeared in the meantime,
# it'll log an error to syslog.
action_doing "Listing pre-existing sudoers.d files before in-place regeneration"
oldsudoers=$(mktemp)
find "$SUDOERS_DIR/" -name "osh-*" -type f > "$oldsudoers"
nbfiles=$(wc -l < "$oldsudoers")
action_done "Found $nbfiles sudoers.d files"
# copy new sudoers files
action_doing "Copy sudoers files to $SUDOERS_DIR"
for file in "$basedir/etc/sudoers.d"/osh-*; do
filename=$(basename "$file")
sed_compat "/\/$filename$/d" "$oldsudoers"
action_detail "... copying $filename"
# don't use install(1) because it doesn't overwrite in place (it unlinks then copies data)
# this can lead to a race condition if somebody uses sudo exactly at this moment, where it'll
# log a bunch of errors because files it has listed from sudoers.d have disappeared
# use a .tmp file while we're setting the proper perms, files containing '.' are ignored by sudo
if ! cp "$file" "$SUDOERS_DIR/$filename.tmp"; then
action_error "Failed copying $file to $SUDOERS_DIR/$filename.tmp"
continue
fi
if ! chown "$UID0":"$GID0" "$SUDOERS_DIR/$filename.tmp"; then
action_error "Failed chowing $SUDOERS_DIR/$filename.tmp to $UID0:$GID0"
# attempt to continue
fi
if ! chmod 0440 "$SUDOERS_DIR/$filename.tmp"; then
action_error "Failed chmoding $SUDOERS_DIR/$filename.tmp to 0440"
# attempt to continue
fi
# then overwrite in place
if ! mv -f "$SUDOERS_DIR/$filename.tmp" "$SUDOERS_DIR/$filename"; then
action_error "Failed to move $SUDOERS_DIR/$filename.tmp to $SUDOERS_DIR/$filename"
fi
done
action_done
# regenerate all group sudoers files
OLD_SUDOERS="$oldsudoers" "$basedir/bin/sudogen/generate-sudoers.sh" create group
# regenerate all accounts sudoers files
OLD_SUDOERS="$oldsudoers" "$basedir/bin/sudogen/generate-sudoers.sh" create account
action_doing "Removing obsolete sudoers.d files if any..."
nbtoremove=$(wc -l < "$oldsudoers")
if [ "$nbtoremove" = 0 ]; then
action_na
else
for toremove in $(< "$oldsudoers")
do
action_detail "removing $toremove"
rm -f "$toremove"
done
action_done "removed $nbtoremove obsolete files"
fi
rm -f "$oldsudoers"
# create the bastionsync account (needed for master/slave)
action_doing "Creating the bastionsync account"
if getent passwd bastionsync >/dev/null 2>&1; then
action_na
else
if ! groupadd_compat bastionsync 333; then
action_error "Error while adding bastionsync group"
elif ! useradd_compat bastionsync 333 '' "$basedir/bin/shell/bastion-sync-helper.sh" 333; then
action_error "Error while adding bastionsync user"
else
action_done
fi
fi
# create some needed accounts
action_doing "Regenerating groups sudoers files from current template"
for i in keykeeper allowkeeper keyreader proxyhttp
do
action_doing "Create $i if needed"
if getent passwd $i >/dev/null ; then
action_na "this account already exists"
else
action_detail "account doesn't exist, creating"
useradd_compat "$i" "" "/home/$i" "/bin/false"
action_done
fi
done
chmod 0750 /home/keyreader /home/proxyhttp
chmod 0755 /home/allowkeeper /home/keykeeper
chmod 0755 "$basedir"/bin/admin/fixrights.sh
"$basedir"/bin/admin/fixrights.sh
# create passkeeper
action_doing "Create /home/passkeeper if needed"
if [ ! -d /home/passkeeper ] ; then
mkdir /home/passkeeper
action_done
else
action_na
fi
chmod 0755 /home/passkeeper
# add groups for specific modules
action_doing "Create needed system groups"
at_least_one_changed=0
for i in superowner admin auditor \
$(find "$basedir/bin/plugin/restricted" -mindepth 1 -maxdepth 1 -type f -perm -u+x -print | sed -e "s=$basedir/bin/plugin/restricted/==")
do
i="osh-$i"
if getent group "$i" >/dev/null ; then
:
else
at_least_one_changed=1
groupadd_compat "$i"
action_detail "$i created"
fi
done
if [ "$at_least_one_changed" = 1 ]; then
action_done
else
action_na
fi
# lastoshuser
# ensures that users created without specifying IDs will be created
# with higher IDs than the lastoshuser UID
# first, we check if it exists with the old name
action_doing "Checking if user lastoshuser exists with a deprecated name"
if getent passwd lastovhuser >/dev/null; then
# yes, ok, do we have also the new name?
if getent passwd lastoshuser >/dev/null; then
# yes, ok, just delete the old name
action_detail "... yes, but also exists with the new name, deleting the old one"
if userdel lastovhuser; then
action_done
else
action_error
fi
else
# no, ok, rename it
action_detail "... no, renaming it to the new name"
if usermod -l lastoshuser lastovhuser; then
action_done
else
action_error
fi
fi
else
action_na
fi
# lastoshuser
# ... do the same for lastoshuser's main gid
# first, we check if it exists with the old name
action_doing "Checking if group lastoshuser exists with a deprecated name"
if getent group lastovhuser >/dev/null; then
# yes, ok, do we have also the new name?
if getent group lastoshuser >/dev/null; then
# yes, ok, just delete the old name
action_detail "... yes, but also exists with the new name, deleting the old one"
if groupdel lastovhuser; then
action_done
else
action_error
fi
else
# no, ok, rename it
action_detail "... no, renaming it to the new name"
if group_rename_compat lastovhuser lastoshuser; then
action_done
else
action_error
fi
fi
else
action_na
fi
# if user exists already, change its ID (old versions had uid 2999)
action_doing "Create user lastoshuser"
lastoshuseruid=$(getent passwd lastoshuser | cut -d: -f3)
if [ -n "$lastoshuseruid" ] && [ "$lastoshuseruid" != "10000" ] ; then
action_detail "user exists but with bad UID ($lastoshuseruid), fixing"
if usermod_changeuid_compat lastoshuser 10000; then
action_done
else
action_error "Error while attempting to change lastoshuser UID"
fi
elif [ -n "$lastoshuseruid" ] && [ "$lastoshuseruid" = "10000" ] ; then
action_na "user exists with proper UID"
else
action_detail "doesn't exist, creating it"
useradd_compat lastoshuser 10000 /nonexistent /bin/false
action_done
fi
# ensure lastoshuser main group has a gid of 10000
action_doing "Adjust gid of lastoshuser if needed"
lastoshgid=$(getent group lastoshuser | cut -d: -f3)
if [ -n "$lastoshgid" ] && [ "$lastoshgid" != "10000" ] ; then
action_detail "group exists but with bad GID ($lastoshgid), fixing"
if group_change_gid_compat lastoshuser 10000; then
action_done
else
action_error "Error while attempting to change lastoshuser GID"
fi
elif [ -n "$lastoshgid" ] && [ "$lastoshgid" = "10000" ] ; then
action_na "user exists with proper GID"
else
action_error "No group lastoshuser was found (!)"
fi
action_doing "Create /var/log/bastion if needed"
if [ ! -d /var/log/bastion ] ; then
mkdir -p /var/log/bastion
action_done
else
action_na
fi
action_doing "Set proper rights on /var/log/bastion"
chown "$UID0":allowkeeper /var/log/bastion
chmod 0710 /var/log/bastion
action_done
# ensuring proper ACLs on group homes
action_doing "Ensuring proper ACLs on group homes and allowed.ip"
for grp in $(getent group | cut -d: -f1 | grep -- '-gatekeeper$' | sed -e 's/-gatekeeper$//'); do
test -f "/home/$grp/allowed.ip" || continue
if [ "$OS_FAMILY" = "Linux" ] || [ "$OS_FAMILY" = "FreeBSD" ]; then
setfacl -m "group:osh-whoHasAccessTo:--x" "/home/$grp"
setfacl -m "group:osh-auditor:--x" "/home/$grp"
setfacl -m "group:osh-superowner:--x" "/home/$grp"
setfacl -m "group:$grp-gatekeeper:--x" "/home/$grp"
setfacl -m "group:$grp-aclkeeper:--x" "/home/$grp"
setfacl -m "group:$grp-owner:--x" "/home/$grp"
fi
chgrp "$grp-aclkeeper" "/home/$grp/allowed.ip"
chmod 0664 "/home/$grp/allowed.ip"
done
action_done
# ensuring proper ACLs on account homes
action_doing "Ensuring proper ACLs on account homes"
for accounthome in $(getent passwd | grep ":$basedir/bin/shell/osh.pl$" | cut -d: -f6); do
if test -d "$accounthome"; then
chmod 0750 "$accounthome"
setfacl -m g:osh-auditor:x "$accounthome"
fi
if test -d "$accounthome/.ssh"; then
setfacl -m g:osh-auditor:x "$accounthome/.ssh"
fi
done
action_done
# v2.27.02+ (bastion-users), v2.30.00+ (mfa-*)
action_doing "Creating missing system groups where needed"
at_least_one_changed=0
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 osh-pubkey-auth-optional
do
if getent group "$group" >/dev/null 2>&1; then
:
else
action_detail "... $group"
if groupadd_compat "$group"; then
at_least_one_changed=1
else
at_least_one_error=1
fi
fi
done
if [ "$at_least_one_error" != 0 ]; then
action_error
elif [ "$at_least_one_changed" = 0 ]; then
action_na
else
action_done
fi
# create logkeeper
action_doing "Create /home/logkeeper if needed"
if [ ! -d /home/logkeeper ] ; then
mkdir /home/logkeeper
action_done
else
action_na
fi
chown root:bastion-users /home/logkeeper
chmod 0730 /home/logkeeper
# create the healthcheck account for the monitoring probe (doesn't harm even if not used)
action_doing "Creating the healthcheck account"
if getent passwd healthcheck >/dev/null 2>&1; then
action_na
else
ssh-keygen -q -t ed25519 -N '' -f "$UID0HOME/id_healthcheck" || \
ssh-keygen -q -t ecdsa -N '' -f "$UID0HOME/id_healthcheck" || \
ssh-keygen -q -t rsa -b 4096 -N '' -f "$UID0HOME/id_healthcheck"
if [ ! -e "$UID0HOME/id_healthcheck.pub" ]; then
action_error "Error while generating the SSH key"
else
chmod 0444 "$UID0HOME/id_healthcheck.pub"
USER="$UID0" HOME="$UID0HOME" "$basedir"/bin/plugin/restricted/accountCreate '' '' '' '' --account healthcheck --uid-auto --always-active --immutable-key --osh-only --force-key-from "127.0.0.1" < "$UID0HOME/id_healthcheck.pub"
if ! getent passwd healthcheck >/dev/null 2>&1; then
action_error "Couldn't create the healthcheck account"
else
mv "$UID0HOME/id_healthcheck" ~healthcheck/.ssh/id_healthcheck
mv "$UID0HOME/id_healthcheck.pub" ~healthcheck/.ssh/id_healthcheck.pub
chown healthcheck:healthcheck ~healthcheck/.ssh/id_healthcheck ~healthcheck/.ssh/id_healthcheck.pub
chmod 400 ~healthcheck/.ssh/id_healthcheck
action_done
fi
fi
fi
# ensure the system bastion accounts are in bastion-nopam group
at_least_one_changed=0
at_least_one_error=0
action_doing "Ensuring bastionsync and healthcheck are in bastion-nopam group"
for account in bastionsync healthcheck; do
if ! getent group bastion-nopam | grep -q -E "[:,]$account(,|$)"; then
if add_user_to_group_compat $account bastion-nopam; then
at_least_one_changed=1
else
at_least_one_error=1
fi
fi
done
if [ "$at_least_one_error" != 0 ]; then
action_error
elif [ "$at_least_one_changed" = 0 ]; then
action_na
else
action_done
fi
# create the master2slave ssh key (only needed for master, useless but not harmful for slaves)
action_doing "Generating the master/slave SSH key"
if [ -e "$UID0HOME/.ssh/id_master2slave" ]; then
action_na
else
ssh-keygen -q -t ed25519 -N '' -f "$UID0HOME/.ssh/id_master2slave" || \
ssh-keygen -q -t ecdsa -N '' -f "$UID0HOME/.ssh/id_master2slave" || \
ssh-keygen -q -t rsa -b 4096 -N '' -f "$UID0HOME/.ssh/id_master2slave"
if [ ! -e "$UID0HOME/.ssh/id_master2slave.pub" ]; then
action_error "Error while generating the SSH key"
else
action_done
fi
fi
# create the ssh key for backups
action_doing "Generating the backup SSH key"
if [ -e "$UID0HOME/.ssh/id_backup" ]; then
action_na
else
ssh-keygen -q -t ed25519 -N '' -f "$UID0HOME/.ssh/id_backup" || \
ssh-keygen -q -t ecdsa -N '' -f "$UID0HOME/.ssh/id_backup" || \
ssh-keygen -q -t rsa -b 4096 -N '' -f "$UID0HOME/.ssh/id_backup"
if [ ! -e "$UID0HOME/.ssh/id_backup.pub" ]; then
action_error "Error while generating the SSH key"
else
action_done
fi
fi
# grant the bastion admins to all the restricted commands, as we might have added new ones
action_doing "Granting all the restricted commands to the bastion admins"
admins=$(perl -I"$basedir/lib/perl" -MOVH::Bastion -e 'my $admins = OVH::Bastion::config("adminAccounts")->value; print join("\n", @{ $admins || [] })')
grant_status=0
for account in $(getent group osh-admin | cut -d: -f4 | tr "," " ")
do
# we must also check that this account is declared in bastion.conf (or it's not really an admin),
if ! echo "$admins" | grep -qw "$account"; then
action_detail "$account is not really an admin, skipping"
continue
fi
action_detail "$account"
_before_change=$(id "$account")
"$basedir/bin/admin/grant-all-restricted-commands-to.sh" "$account" >/dev/null; ret=$?
_after_change=$(id "$account")
if [ "$ret" -ne 0 ]; then
action_detail "... failed!"
grant_status=-1
elif [ "$_before_change" = "$_after_change" ]; then
action_detail "... nothing to do"
else
action_detail "... done"
[ "$grant_status" != -1 ] && grant_status=1
fi
done
if [ "$grant_status" = 0 ]; then
action_na
elif [ "$grant_status" = -1 ]; then
action_error
else
action_done
fi
action_doing "Ensuring all bastion accounts are in the bastion-users group"
bastion_users_members=$(getent group bastion-users | cut -d: -f4 | tr , " ")
at_least_one_changed=0
at_least_one_error=0
for account in $(getent passwd | grep ":$basedir/bin/shell/osh.pl$" | cut -d: -f1) proxyhttp
do
if ! echo "$bastion_users_members" | grep -q -w "$account"; then
action_detail "... $account"
add_user_to_group_compat "$account" "bastion-users" || at_least_one_error=1
at_least_one_changed=1
fi
done
if [ "$at_least_one_error" != 0 ]; then
action_error
elif [ "$at_least_one_changed" = 0 ]; then
action_na
else
action_done
fi
action_doing "Archiving old account and group files"
at_least_one_changed=0
at_least_one_error=0
acls_param=''
[ "$OS_FAMILY" = "Linux" ] && acls_param='--acls'
[ "$OS_FAMILY" = "FreeBSD" ] && acls_param='--acls'
while IFS= read -r -d '' dir
do
at_least_one_changed=1
if command -v chattr &>/dev/null; then
find "$dir" -name "*.log" -print0 | xargs -r0 chattr -a || true
fi
tarfile="$dir.tar.gz"
while [ -e "$tarfile" ]; do
# archive already exists? append some randomness to the name, we don't want to overwrite it!
tarfile="$dir.$(date +%s)-$RANDOM.tar.gz"
done
if tar czf "$tarfile" $acls_param --one-file-system -p --remove-files "$dir"; then
chmod 0 "$tarfile"
else
at_least_one_error=1
fi
done < <(find /home/oldkeeper/accounts/ /home/oldkeeper/groups/ -mindepth 1 -maxdepth 1 -type d -print0 2>/dev/null)
if [ "$at_least_one_error" != 0 ]; then
action_error
elif [ "$at_least_one_changed" = 0 ]; then
action_na
else
action_done
fi
action_doing "Replacing legacy o+w by bastion-users/g+w"
at_least_one_changed=0
for file in \
$(test -f /home/osh.log && find /home/osh.log \( -perm -o+w -o ! -user root \) ) \
$(find /home/logkeeper/ -mindepth 1 -maxdepth 1 -type f -name "global-log-*" \( -perm -o+w -o ! -user root \) )
do
if echo "$file" | grep -Eq '\.log$' && command -v chattr &>/dev/null; then
chattr -a "$file" 2>/dev/null || true
fi
chown root:bastion-users "$file"
chmod 660 "$file"
if echo "$file" | grep -Eq '\.log$' && command -v chattr &>/dev/null; then
chattr +a "$file" 2>/dev/null || true
fi
at_least_one_changed=1
done
if [ "$at_least_one_changed" = 0 ]; then
action_na
else
action_done
fi
action_doing "Ensuring symlinked plugins have their json config"
at_least_one_changed=0
at_least_one_error=0
while IFS= read -r -d '' plugin
do
if [ -e "${plugin}.json" ]; then
# the config is there, ok
continue
fi
# the config is not there, is there a corresponding .json at the other end of the symlink?
source=$(readlink "$plugin")
sourcecanon="$(dirname "$plugin")/$source"
sourcecanon="$(readlink -f "$sourcecanon")"
if [ -e "${sourcecanon}.json" ]; then
action_detail "... ${sourcecanon}.json found, symlinking"
ln -s "${source}.json" "${plugin}.json"; ret=$?
if [ $ret -ne 0 ]; then
at_least_one_error=1
else
at_least_one_changed=1
fi
fi
done < <(find "$basedir"/bin/plugin -type l ! -name "*.*" -print0)
if [ "$at_least_one_error" != 0 ]; then
action_error
elif [ "$at_least_one_changed" = 0 ]; then
action_na
else
action_done
fi
# The Bastion > v3.04.00
action_doing "Migrating account creation metadata to new format where applicable"
at_least_one_changed=0
at_least_one_error=0
for accounthome in $(getent passwd | grep ":$basedir/bin/shell/osh.pl$" | cut -d: -f6); do
# if the old AND the new files exists, don't do anything: we won't delete the old file
# because we want our users to be able to roll back to an old version, and as this file doesn't
# hurt anything (it'll just get ignored by new versions), no point in inserting an incompatible change here.
if [ -f "$accounthome/accountCreate.comment" ] && ! [ -f "$accounthome/config.creation_info" ]; then
at_least_one_changed=$((at_least_one_changed + 1))
# build a config.creation_info file from the data stored in the legacy accountCreate.comment
perl -Mstrict -MJSON -mPOSIX -e '
my %h;
open(my $oldfh, "<", $ARGV[0]) or die $!;
while (<$oldfh>) {
chomp;
my @f = split("=", $_, 2);
$h{$f[0]}=$f[1] if @f == 2;
}
close($oldfh);
open(my $newfh, ">", $ARGV[1]) or die $!;
print $newfh encode_json({
by => $h{CREATED_BY},
bastion_version => $h{BASTION_VERSION},
datetime_utc => POSIX::strftime("%a %Y-%m-%d %H:%M:%S UTC", gmtime($h{CREATION_TIMESTAMP})),
datetime_local => POSIX::strftime("%a %Y-%m-%d %H:%M:%S %Z", localtime($h{CREATION_TIMESTAMP})),
timestamp => $h{CREATION_TIMESTAMP}+0,
comment => (($h{COMMENT} && $h{COMMENT} ne "(no_comment_provided)") ? $h{COMMENT} : undef),
});
close($newfh);
' "$accounthome/accountCreate.comment" "$accounthome/config.creation_info"; ret=$?
[ -f "$accounthome/config.creation_info" ] && chmod 644 "$accounthome/config.creation_info"
[ "$ret" != 0 ] && at_least_one_error=1
fi
done
if [ "$at_least_one_error" != 0 ]; then
action_error
elif [ "$at_least_one_changed" = 0 ]; then
action_na
else
action_done "Migrated $at_least_one_changed files"
fi
# checking whether we have things to install from the install.d directory
# shellcheck disable=SC2034
STARTED_BY_MAIN_INSTALL=1
while IFS= read -r -d '' installfile
do
action_doing "Starting module install ($installfile)..."
# shellcheck disable=SC1090
if . "$installfile"; then
action_done
else
action_error
fi
done < <(test -d "$basedir"/install/modules && find "$basedir"/install/modules -mindepth 2 -maxdepth 2 -name install -type f -print0)
unset STARTED_BY_MAIN_INSTALL
fi
if [ "${opt[syslog-ng]}" = 1 ] && [ "${opt[overwrite-syslog-ng]}" = 1 ]; then
if [ -e /etc/syslog-ng/syslog-ng.conf ] && ! grep -q s_src /etc/syslog-ng/syslog-ng.conf && grep -q s_sys /etc/syslog-ng/syslog-ng.conf ; then
sed_compat 's/s_src/s_sys/g' /etc/syslog-ng/conf.d/20-bastion.conf
fi
fi
if [ "$autodetect_startup_system" = 1 ]; then
action_doing "Autodetecting startup system..."
if command -v systemctl >/dev/null && [ -d "/var/run/systemd" ]; then
action_done "found systemd"
opt[systemd-units]=1
opt[init]=0
else
action_done "found sysV-style init"
opt[systemd-units]=0
opt[init]=1
fi
fi
if [ "${opt[init]}" = 1 ]; then
initd=rc.d
[ "$OS_FAMILY" = Linux ] && initd=init.d
action_doing "Copy init scripts to /etc/$initd"
for file in "$basedir/etc/init.d"/osh-*; do
servicename=$(basename "$file")
if [ -e "/etc/$initd/$servicename" ]; then
isnew=0
else
isnew=1
fi
if install -o "$UID0" -g "$GID0" -m 0755 "$file" "/etc/$initd/"; then
if [ "$isnew" = 1 ]; then
action_detail "$servicename installed, ${WHITE_ON_BLUE}please use \`update-rc.d $servicename defaults' if you want to enable this service${NOC}"
else
action_detail "$servicename updated"
fi
else
action_error "failed installing $file"
fi
done
action_done
# remove obsolete init.d files if needed
action_doing "Remove obsolete init.d files..."
at_least_one_changed=0
# shellcheck disable=SC2043
for obsolete in osh-proxy-http
do
if [ -e "/etc/$initd/$obsolete" ]; then
at_least_one_changed=1
action_detail "removing $obsolete"
update-rc.d -f $obsolete disable || true
rm -f "/etc/$initd/$obsolete"
fi
done
if [ "$at_least_one_changed" = 1 ]; then
action_done
else
action_na
fi
fi
if [ "${opt[systemd-units]}" = 1 ]; then
action_doing "Copy systemd unit files to /etc/systemd/system"
for file in "$basedir/etc/systemd"/osh-*.service; do
servicename="$(basename "$file")"
if [ -e "/etc/systemd/system/$servicename" ]; then
isnew=0
else
isnew=1
fi
if cp "$file" /etc/systemd/system/; then
if [ "$isnew" = 1 ]; then
action_detail "$servicename installed, ${WHITE_ON_BLUE}please use \`systemctl enable $servicename' if you want to enable this service${NOC}"
else
action_detail "$servicename updated"
fi
else
action_error "failed installing $file"
fi
done
systemctl daemon-reload || true
action_done
fi
if [ "$autodetect_selinux" = 1 ]; then
action_doing "Autodetecting SELinux..."
if command -v setenforce >/dev/null; then
action_done "found"
opt[install-selinux-module]=1
else
action_done "not found"
opt[install-selinux-module]=0
fi
fi
if [ "${opt[install-selinux-module]}" = 1 ]; then
action_doing "Installing SELinux module"
if ! command -v semodule >/dev/null; then
action_error "Missing \`semodule' tool, please install it (it's usually found in the 'policycoreutils' package)"
else
if semodule -l | grep -q the-bastion; then
action_na "module is already installed"
else
if semodule -i "$basedir/etc/selinux/the-bastion.pp"; then
action_done "module installed"
else
action_error "semodule returned an error"
fi
fi
fi
fi
if [ "${opt[profile]}" = 1 ]; then
action_doing "Copy profile.d files if applicable"
if [ -d $ETC_DIR/profile.d ]; then
for file in "$basedir/etc/profile.d"/*.sh; do
action_detail "$file"
install -o "$UID0" -g "$GID0" -m 0755 "$file" "$ETC_DIR/profile.d/"
done
action_done
else
action_na "$ETC_DIR/profile.d not found"
fi
fi
if [ "${opt[modify-umask]}" = 1 ]; then
# umask
action_doing "Adjust umask in /etc/login.defs if applicable"
if [ -e /etc/login.defs ]; then
sed_compat 's/^(\s*UMASK\s+).+/\1027/g' /etc/login.defs
action_done
else
action_na
fi
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' \
$PAM_DIR/common-session ; then
action_detail "missing umask config in file, adjusting"
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"
fi
else
action_na "no file found"
fi
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' "$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 [ "$LINUX_DISTRO" = ubuntu ] && [ "$DISTRO_VERSION_MAJOR" -ge 22 ]; then
pamsuffix="ubuntu2204"
elif [ -n "$pamsuffix" ] && [ -e "$basedir/etc/pam.d/sshd.$pamsuffix$DISTRO_VERSION" ]; then
pamsuffix="$pamsuffix$DISTRO_VERSION"
fi
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 "sshd.$pamsuffix"
else
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!)"
fi
fi
if [ "${opt[modify-pam-lastlog]}" = 1 ]; then
# pam.d lastlogin
action_doing "Adjust lastlog in pam.d/sshd if applicable"
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 '/^[[:space:]]*@include[[:space:]]+common-session/a\
# bastion config: lastlog needs to be updated on connection\
session optional pam_lastlog.so silent' "$PAM_SSHD"
action_done
else
action_na "lastlog config was already ok"
fi
else
action_na "no file found"
fi
fi
if [ "${opt[remove-weak-moduli]}" = 1 ]; then
# remove low moduli
action_doing "Remove weak moduli (less than 4K)"
if [ -e $SSH_DIR/moduli ] ; then
tmpmod=$(mktemp)
awk '$5 >= 4095' $SSH_DIR/moduli > "$tmpmod"
if cmp -s $SSH_DIR/moduli "$tmpmod"; then
action_na "no weak moduli found"
else
cat "$tmpmod" > $SSH_DIR/moduli
action_done
fi
rm -f "$tmpmod"
fi
fi
if [ "${opt[generate-mfa-secret]}" = 1 ]; then
action_doing "Generate the MFA HMAC secret if needed"
if [ -e "$BASTION_ETC_DIR/mfa-token.conf" ]; then
action_na
else
secret=$(env LANG=C tr -dc A-Za-z0-9 < /dev/urandom 2>/dev/null | head -c32)
touch "$BASTION_ETC_DIR/mfa-token.conf"
chown 0:bastion-users "$BASTION_ETC_DIR/mfa-token.conf"
chmod 640 "$BASTION_ETC_DIR/mfa-token.conf"
echo '{ "secret": "'"$secret"'" }' > "$BASTION_ETC_DIR/mfa-token.conf"
action_done
fi
fi
# lastly, check for ttyrec version and yell if it's not the proper one
if [ "${opt[check-ttyrec]}" = 1 ] ; then
action_doing "Checking ttyrec version"
if ! command -v ttyrec >/dev/null 2>&1; then
action_error "ttyrec is not installed, the bastion will not work! Please either install ovh-ttyrec (/opt/bastion/bin/admin/install-ttyrec.sh) or run this script a second time with \`$0 --nothing --install-fake-ttyrec'"
else
ttyrec_version=$(ttyrec -V 2>/dev/null | grep -Eo 'ttyrec v[0-9.]+' | cut -c9-)
if [ -z "$ttyrec_version" ]; then
action_error "Incompatible ttyrec version installed, the bastion will not work! Please either install ovh-ttyrec (/opt/bastion/bin/admin/install-ttyrec.sh) or run this script again with \`$0 --nothing --install-fake-ttyrec'"
else
action_detail "found v$ttyrec_version"
action_detail "expected v$TTYREC_VERSION_NEEDED"
if [ "$(printf "%s\\n%s\\n" "$ttyrec_version" "$TTYREC_VERSION_NEEDED" | sort | head -1)" = "$TTYREC_VERSION_NEEDED" ]; then
action_done
else
action_error "The installed ttyrec version is too old, the bastion will not work! Please update ovh-ttyrec to at least v$TTYREC_VERSION_NEEDED (/opt/bastion/bin/admin/install-ttyrec.sh can help)"
fi
fi
fi
fi
# used in trap_exit
script_end_reached=1