Skip to content

Commit

Permalink
feat: add undocumented rename-account.sh and modify osh-orphaned-home…
Browse files Browse the repository at this point in the history
…dir.sh accordingly
  • Loading branch information
speed47 committed Feb 5, 2025
1 parent 87d1655 commit 1939098
Show file tree
Hide file tree
Showing 6 changed files with 252 additions and 38 deletions.
2 changes: 1 addition & 1 deletion bin/admin/fix-group-gid.sh
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ main()
fi

echo
echo "$group: OK to proceed ? (CTRL+C to abort). You'll still have to validate each commands I'm going to run"
echo "$group: OK to proceed? (CTRL+C to abort). You'll still have to validate each commands I'm going to run"
# shellcheck disable=SC2034
read -r ___
really_run_commands=1
Expand Down
109 changes: 109 additions & 0 deletions bin/admin/rename-account.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
#! /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

from=$1
to=$2

if [ -n "$3" ] || [ -z "$2" ] ; then
echo "Usage: $0 original_account_name new_account_name"
exit 2
fi

fail()
{
echo "Error, will not proceed: $*"
exit 1
}

really_run_commands=0

finalret=0
_run()
{
local ret
if [ "$really_run_commands" = "1" ] ; then
echo "Executing: $*"
"$@"; ret=$?
echo "... return code: $ret"
if [ "$ret" -ne 0 ]; then
(( ++finalret ))
fi
else
echo "DRY RUN: would execute: $*"
fi
}

batchrun()
{
# first, rename the main account group
getent group "$from" >/dev/null || fail "group $from doesn't exist"
getent group "$to" >/dev/null && fail "group $to already exists"
if [ "$OS_FAMILY" = FreeBSD ]; then
_run pw groupmod -n "$from" -l "$to"
else
_run groupmod -n "$to" "$from"
fi

# then, rename the account itself
getent passwd "$from" >/dev/null || fail "user $from doesn't exist"
getent passwd "$to" >/dev/null && fail "user $to already exists"
test -d "/home/$from" || fail "directory /home/$from doesn't exist"
test -d "/home/$to" && fail "directory /home/$to already exists"
if [ "$OS_FAMILY" = FreeBSD ]; then
# FreeBSD doesn't move the home on rename, do it ourselves
_run mv -v "/home/$from" "/home/$to"
_run pw usermod -n "$from" -d /home/"$to" -l "$to"
else
_run usermod -m -d /home/"$to" -l "$to" "$from"
fi

# then, rename all other groups linked to the account (appart from the main one already done)
# shellcheck disable=SC2043
for suffix in tty; do
if getent group "$from-$suffix" >/dev/null ; then
getent group "$to-$suffix" >/dev/null && fail "group $to-$suffix already exists"
if [ "$OS_FAMILY" = FreeBSD ]; then
_run pw groupmod -n "$from-$suffix" -l "$to-$suffix"
else
_run groupmod -n "$to-$suffix" "$from-$suffix"
fi
fi
done

# now handle the allowkeeper folder of the account
test -d "/home/allowkeeper/$from" || fail "directory /home/allowkeeper/$from doesn't exist"
test -d "/home/allowkeeper/$to" && fail "directory /home/allowkeeper/$to already exists"
_run mv -v "/home/allowkeeper/$from" "/home/allowkeeper/$to"

# if the account has passwords, handle them: filenames contain the account name
passdir="/home/$from/pass"
[ "$really_run_commands" = "1" ] && passdir="/home/$to/pass"
if [ -d "$passdir" ]; then
pushd "$passdir" >/dev/null || exit 1
# shellcheck disable=SC2044
for passfile in $(find . -type f -name "$from" -print -o -type f -name "$from.*" -print)
do
_run mv -v "$passfile" "${passfile/$from/$to}"
done
popd >/dev/null || exit 1
fi

# finally, regenerate the account's sudoers
_run "$basedir"/bin/sudogen/generate-sudoers.sh create account "$to"
_run "$basedir"/bin/sudogen/generate-sudoers.sh delete account "$from"
}


really_run_commands=0
batchrun
echo
echo "OK to proceed? (CTRL+C to abort)"
# shellcheck disable=SC2034
read -r ___
really_run_commands=1
batchrun
echo "Done."
exit $finalret
30 changes: 26 additions & 4 deletions bin/admin/rename-group.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#! /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

from=$1
to=$2
Expand All @@ -18,13 +20,17 @@ fail()

really_run_commands=0

finalret=0
_run()
{
local ret
if [ "$really_run_commands" = "1" ] ; then
echo "Executing: $*"
"$@"; ret=$?
echo "... return code: $ret"
if [ "$ret" -ne 0 ]; then
(( ++finalret ))
fi
else
echo "DRY RUN: would execute: $*"
fi
Expand All @@ -34,18 +40,33 @@ batchrun()
{
getent group "key$from" >/dev/null || fail "group key$from doesn't exist"
getent group "key$to" >/dev/null && fail "group key$to already exists"
_run groupmod -n "key$to" "key$from"
if [ "$OS_FAMILY" = FreeBSD ]; then
_run pw groupmod -n "key$from" -l "key$to"
else
_run groupmod -n "key$to" "key$from"
fi


getent passwd "key$from" >/dev/null || fail "user key$from doesn't exist"
getent passwd "key$to" >/dev/null && fail "user key$to already exists"
test -d "/home/key$from" || fail "directory /home/key$from doesn't exists"
test -d "/home/key$to" && fail "directory /home/key$to already exists"
_run usermod -m -d /home/"key$to" -l "key$to" "key$from"
if [ "$OS_FAMILY" = FreeBSD ]; then
# FreeBSD doesn't move the home on rename, do it ourselves
_run mv -v "/home/key$from" "/home/key$to"
_run pw usermod -n "key$from" -d /home/"key$to" -l "key$to"
else
_run usermod -m -d /home/"key$to" -l "key$to" "key$from"
fi

for suffix in gatekeeper aclkeeper owner; do
if getent group "key$from-$suffix" >/dev/null ; then
getent group "key$to-$suffix" >/dev/null && fail "group key$to-$suffix already exists"
_run groupmod -n "key$to-$suffix" "key$from-$suffix"
if [ "$OS_FAMILY" = FreeBSD ]; then
_run pw groupmod -n "key$from-$suffix" -l "key$to-$suffix"
else
_run groupmod -n "key$to-$suffix" "key$from-$suffix"
fi
fi
done

Expand Down Expand Up @@ -94,9 +115,10 @@ _run "$basedir"/bin/sudogen/generate-sudoers.sh delete group "key$from"
really_run_commands=0
batchrun
echo
echo "OK to proceed ? (CTRL+C to abort)"
echo "OK to proceed? (CTRL+C to abort)"
# shellcheck disable=SC2034
read -r ___
really_run_commands=1
batchrun
echo "Done."
exit $finalret
106 changes: 75 additions & 31 deletions bin/cron/osh-orphaned-homedir.sh
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,46 @@ esac

mkdir -p "/home/oldkeeper/orphaned"

archive_dir() {
_dir="$1"

archive="/home/oldkeeper/orphaned/$(basename "$_dir").at-$(date +%s).by-orphaned-homedir-script.tar.gz"
_log "Found orphaned $_dir [$(ls -ld "$_dir")], archiving..."
chmod 0700 /home/oldkeeper/orphaned
if [ "$OS_FAMILY" = "Linux" ]; then
find "$_dir" -mindepth 1 -maxdepth 1 -type f -name "*.log" -print0 | xargs -r0 chattr -a
fi

# remove empty directories if we have some
find "$_dir" -type d -delete 2>/dev/null || true
acls_param=''
[ "$OS_FAMILY" = "Linux" ] && acls_param='--acls'
[ "$OS_FAMILY" = "FreeBSD" ] && acls_param='--acls'

set +e
tar czf "$archive" $acls_param --one-file-system -p --remove-files --exclude=ttyrec "$_dir" 2>/dev/null; ret=$?
set -e

if [ $ret -ne 0 ]; then
# $? can be 2 if we can't delete because ttyrec dir remains so it might not be a problem
if [ $ret -eq 2 ] && [ -s "$archive" ] && [ -d "$_dir" ] && [ "$(find "$_dir" -name ttyrec -prune -o -print | wc -l)" = 1 ]; then
# it's OK. We chown all to root to avoid orphan UID/GID and we let the backup script take care of those
# if we still have files under $dir/ttyrec, chown all them to root:root to avoid orphan UID/GID,
# and just wait for them to be encrypted/rsynced out of the bastion by the usual ttyrec archiving script
_log "Archived $_dir to $archive"
chmod 0 "$archive"

chown -R root:root "$_dir"
_warn "Some files remain in $_dir, we chowned everything to root"
else
_err "Couldn't archive $_dir to $archive"
fi
else
_log "Archived $_dir to $archive"
chmod 0 "$archive"
fi
}

while IFS= read -r -d '' dir
do
# just in case, check ourselves again that the folder's UID/GID don't resolve
Expand Down Expand Up @@ -70,42 +110,46 @@ do
continue
fi

archive="/home/oldkeeper/orphaned/$(basename "$dir").at-$(date +%s).by-orphaned-homedir-script.tar.gz"
_log "Found orphaned $dir [$(ls -ld "$dir")], archiving..."
chmod 0700 /home/oldkeeper/orphaned
if [ "$OS_FAMILY" = "Linux" ]; then
find "$dir" -mindepth 1 -maxdepth 1 -type f -name "*.log" -print0 | xargs -r0 chattr -a
fi
archive_dir "$dir"
done < <(find /home/ -mindepth 1 -maxdepth 1 -type d -nouser -nogroup -mmin +3 -print0)

# remove empty directories if we have some
find "$dir" -type d -delete 2>/dev/null || true
acls_param=''
[ "$OS_FAMILY" = "Linux" ] && acls_param='--acls'
[ "$OS_FAMILY" = "FreeBSD" ] && acls_param='--acls'
# corner case for manually renamed accounts on master: the old homedir will stay on slaves,
# with only logs, ttyrecs and such (files excluded from the cluster sync).
# in that case, the old homedir is owned by the new account name, and there is no corresponding allowkeeper directory.
# look for those, and archive them if found.
if [ "$role" = "slave" ]; then
while IFS= read -r -d '' file
do
# we're getting all the folders with a "lastlog" file, skip all those that are present
# in the proper home of their file owner
file_owner=$(stat -c %U "$file" 2>/dev/null)
[ -n "$file_owner" ] || continue

owner_home=$(getent passwd "$file_owner" 2>/dev/null | cut -d: -f6)
[ -n "$owner_home" ] || continue
[ -d "$owner_home" ] || continue

if [ "$file" = "$owner_home/lastlog" ]; then
# all is good, nothing to see here
continue
fi

set +e
tar czf "$archive" $acls_param --one-file-system -p --remove-files --exclude=ttyrec "$dir" 2>/dev/null; ret=$?
set -e
_log "Found a misowned lastlog file as $file, owned by $file_owner who's home is $owner_home"

if [ $ret -ne 0 ]; then
# $? can be 2 if we can't delete because ttyrec dir remains so it might not be a problem
if [ $ret -eq 2 ] && [ -s "$archive" ] && [ -d "$dir" ] && [ "$(find "$dir" -name ttyrec -prune -o -print | wc -l)" = 1 ]; then
# it's ok. we chown all to root to avoid orphan UID/GID and we let the backup script take care of those
# if we still have files under $dir/ttyrec, chown all them to root:root to avoid orphan UID/GID,
# and just wait for them to be encrypted/rsynced out of the bastion by the usual ttyrec archiving script
_log "Archived $dir to $archive"
chmod 0 "$archive"
orphanhome="$(dirname "$file")"

chown -R root:root "$dir"
_warn "Some files remain in $dir, we chowned everything to root"
else
_err "Couldn't archive $dir to $archive"
# ensure that the only other present files in this home are: ttyrec/*, *.log and *.sqlite
otherfiles=$(find "$orphanhome/" -mindepth 1 -name ttyrec -prune -o -name tmp -prune \
-o ! -name lastlog ! -name "*.log" ! -name "*.sqlite" -print | head -n 1)
if [ -n "$otherfiles" ]; then
_warn "... at least another file has been found in $orphanhome, skipping"
continue
fi
else
_log "Archived $dir to $archive"
chmod 0 "$archive"
fi
done < <(find /home/ -mindepth 1 -maxdepth 1 -type d -nouser -nogroup -mmin +3 -print0)

_log "Proceeding in archiving $orphanhome..."
archive_dir "$orphanhome"
done < <(find /home/ -mindepth 2 -maxdepth 2 -type f -name lastlog -print0)
fi

# there are also temporary files that might not be cleaned when an account disappears,
# so check for those here
Expand Down
41 changes: 40 additions & 1 deletion tests/functional/tests.d/200-scripts.sh
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ testsuite_scripts()
contain "Done"
nocontain "WARN:"
nocontain "ERROR:"
nocontain "Unexpected termination"

# encrypt rsync (nothing to encrypt)

Expand All @@ -116,6 +117,7 @@ testsuite_scripts()
contain "Done"
nocontain "WARN:"
nocontain "ERROR:"
nocontain "Unexpected termination"

# ttyrec subfolders cleanup
success ttyrec_cleanup $r0 /opt/bastion/bin/cron/osh-remove-empty-folders.sh
Expand Down Expand Up @@ -146,9 +148,46 @@ testsuite_scripts()
nocontain "ERROR:"
nocontain "Unexpected termination"

# rename account
script account_rename $r0 /opt/bastion/bin/admin/rename-account.sh $account1 $account2 '</dev/null'
retvalshouldbe 0
contain "Done"
nocontain "WARN:"
nocontain "ERROR:"
nocontain "Unexpected termination"

# rename account a second time
script account_rename2 $r0 /opt/bastion/bin/admin/rename-account.sh $account2 $account3 '</dev/null'
retvalshouldbe 0
contain "Done"
nocontain "WARN:"
nocontain "ERROR:"
nocontain "Unexpected termination"

# cleanup account
success a0_delete_a3 $a0 --osh accountDelete --account $account3 --no-confirm

# create a group to try to rename it
success a0_create_g1 $a0 --osh groupCreate --owner $account0 --algo ed25519 --size 256 --group $group1

# rename the group
script group_rename $r0 /opt/bastion/bin/admin/rename-group.sh $group1 $group2 '</dev/null'
retvalshouldbe 0
contain "Done"
nocontain "WARN:"
nocontain "ERROR:"
nocontain "Unexpected termination"

# rename the group, again
script group_rename2 $r0 /opt/bastion/bin/admin/rename-group.sh $group2 $group3 '</dev/null'
retvalshouldbe 0
contain "Done"
nocontain "WARN:"
nocontain "ERROR:"
nocontain "Unexpected termination"

success a0_delete_a1 $a0 --osh accountDelete --account $account1 --no-confirm
# cleanup group
success a0_delete_g3 $a0 --osh groupDelete --group $group3 --no-confirm
}

testsuite_scripts
Expand Down
2 changes: 1 addition & 1 deletion tests/functional/tests.d/395-mfa-scp-sftp-rsync.sh
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ EOF
## detect recent scp
local scp_options=""
if [ "$COUNTONLY" != 1 ]; then
if scp -O -S /bin/true a: b 2>/dev/null; then
if scp -O -S $(command -v true) a: b 2>/dev/null; then
echo "scp: will use new version params"
scp_options="-O"
else
Expand Down

0 comments on commit 1939098

Please sign in to comment.