Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add undocumented rename-account.sh and modify osh-orphaned-home… #523

Merged
merged 2 commits into from
Feb 5, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/freebsd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
uses: cross-platform-actions/action@master
with:
operating_system: freebsd
version: '13.2'
version: '14.2'
shell: bash
sync_files: runner-to-vm
run: |
Expand All @@ -31,8 +31,8 @@ jobs:
# to do proper tests, we need the fs to have ACLs enabled
sudo mount -o acls /
# install required packages
sudo env IGNORE_OSVERSION=yes pkg update
sudo env IGNORE_OSVERSION=yes pkg install -y bash rsync ca_root_nss jq fping screen flock curl
sudo env pkg update
sudo env pkg install -y bash rsync ca_root_nss jq fping screen flock curl
# create required folder
sudo mkdir -p /opt/bastion
# copy bastion code to the proper location
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,9 +127,9 @@ Any other so-called "modern" Linux version are not tested with each release, but

The following OS are also tested with each release:

- FreeBSD/HardenedBSD 13.2\*\*
- FreeBSD/HardenedBSD 14.2\*\*

\*\*: Note that these have partial MFA support, due to their reduced set of available `pam` plugins. Support for either an additional password or TOTP factor can be configured, but not both at the same time. The code is actually known to work on FreeBSD/HardenedBSD 10+, but it's only regularly tested under 13.2.
\*\*: Note that these have partial MFA support, due to their reduced set of available `pam` plugins. Support for either an additional password or TOTP factor can be configured, but not both at the same time. The code is actually known to work on FreeBSD/HardenedBSD 10+, but it's only regularly tested under 14.2.

Other BSD variants, such as OpenBSD and NetBSD, are unsupported as they have a severe limitation over the maximum number of supplementary groups, causing problems for group membership and restricted commands checks, as well as no filesystem-level ACL support and missing PAM support (hence no MFA).

Expand Down
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
4 changes: 2 additions & 2 deletions doc/sphinx/installation/basic.rst
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,11 @@ but should work with no or minor adjustments.

The following OS are also tested with each release:

- FreeBSD/HardenedBSD 13.2\*\*
- FreeBSD/HardenedBSD 14.2\*\*

\*\*: Note that these have partial MFA support, due to their reduced set of available ``pam`` plugins.
Support for either an additional password or TOTP factor can be configured, but not both at the same time.
The code is actually known to work on FreeBSD/HardenedBSD 10+, but it's only regularly tested under 13.2.
The code is actually known to work on FreeBSD/HardenedBSD 10+, but it's only regularly tested under 14.2.

Other BSD variants, such as OpenBSD and NetBSD, are unsupported as they have a severe limitation over the maximum
number of supplementary groups, causing problems for group membership and restricted commands checks,
Expand Down
Loading