Skip to content

Instantly share code, notes, and snippets.

@rawiriblundell
Created September 13, 2020 00:13
Show Gist options
  • Save rawiriblundell/4ac56e23b253a89c8d65ec2d5579f52f to your computer and use it in GitHub Desktop.
Save rawiriblundell/4ac56e23b253a89c8d65ec2d5579f52f to your computer and use it in GitHub Desktop.
checkmk local check for local account auditing
#!/bin/bash
# check_user_lastseen - report on users who have not logged in for a while
# Purpose:
# This script tries to find idle accounts and any orphaned homedirs
# Currently is Linux biased but capacity for portability is there
# Author: Rawiri Blundell
# Copyright: See provided LICENCE file
###############################################################################
# Source the config mapping library
# Provides variables "${thisHost}", "${thisJob}" and
# positional parameters as "${arg1}" "${arg2}" etc.
# It also provides the functions printDebug, printOK, printWarn and printCrit
if [[ ! -r /opt/checkmk/lib/libconfmap.sh ]]; then
printf '%s\n' "3 ${0##*/} - libconfmap.sh not readable"
exit 0
else
# shellcheck disable=SC1091
. /opt/checkmk/lib/libconfmap.sh
fi
########################################
# Define the maximum time (in days) that an account can sit idle
maxTime="${arg1:-60}"
# Express the above in UNIX epoch time
maxTimeEpoch=$(date -d "${maxTime} days ago" +%s)
# Figure out the lowest boundary for the available UID range
uidMin=$(awk '/^UID_MIN/{print $2}' /etc/login.defs 2>/dev/null)
# Older releases of various Linux distros tended to use '500' as the minimum
# So if we can't find it in login.defs, we'll default to '500'
uidMin="${uidMin:-500}"
# Get the most recent file within a directory ($1 or cwd)
# Output: [unix epoch time] [filename]
get_last_mtime() {
python -c "import glob; import os; \
os.stat_float_times(False); \
list_of_files = glob.glob('${1:-.}/.*'); \
latest_file = max(list_of_files, key=os.path.getmtime); \
stat = os.stat(latest_file); \
print stat.st_mtime, latest_file" 2>/dev/null
}
# Get local users from /etc/passwd
get_local_users() {
awk -F':' -v "min=${uidMin}" '{ if ( $3 >= min && $7 != "/sbin/nologin" ) print $1 }' "/etc/passwd"
}
# Small function to print the user's home directory
get_user_home() {
getent passwd "${1}" | awk -F ':' '{print $6}'
}
# Is a directory orphaned?
is_dir_orphan() {
local dirCount
# Test the supplied directory and count the output
dirCount=$(
find "${1:?No dir supplied}" \( -name . -o -prune \) \
-type d -nouser 2>/dev/null \
| wc -l
)
# If the count is 1 or more, then return 0 (success)
(( dirCount >= 1 )) && return 0
# Under all other conditions, return 1 (failure)
return 1
}
# Find the most recent file within the given user's homedir, print epoch
# This approach is significantly less reliable than the last log approach
test_homedir() {
get_last_mtime "$(get_user_home "${1}")"
}
# Parse the users in /etc/passwd
# See if 'lastlog -b 60' is available and works
if command -v lastlog -b 60 >/dev/null 2>&1; then
# Older versions of 'lastlog' don't support '-u [ran-ge]' so we do it manually
while IFS=$'\n' read -r line; do
idleAcctArray+=("${line}")
done < <(lastlog -b "${maxTime}" | grep -f <(get_local_users) | awk '{print $1}')
# Otherwise, test against the latest modified file in each user's homedir
else
for user in $(get_local_users); do
if (( $(test_homedir "${user}") <= maxTimeEpoch )); then
idleAcctArray+=( "${user}" )
fi
done
fi
# Parse any extra homedirs, this caters for network accounts (e.g. AD)
for homeDir in /home/* /home/users/* /export/home/*; do
# If it's in /etc/passwd, we want to skip it
grep -q ":${homeDir}:" /etc/passwd || continue
# Test to see if it's an orphaned directory, if so,
# add it to the orphan array and skip to the next homeDir
if is_dir_orphan "${homeDir}"; then
orphanArray+=( "${homeDir}" )
continue
fi
# Finally, test the latest modified file against our threshold
if (( $(test_homedir "${homeDir}") <= maxTimeEpoch )); then
idleAcctArray+=( "${homeDir##*/}" )
fi
done
# Compare our findings and output the judgement
if (( "${#idleAcctArray[@]}" >= 1 ))&&(( "${#orphanArray[@]}" == 0 )); then
printWarn "Idle_Accounts=${#idleAcctArray[@]} Idle accounts found" \
"${idleAcctArray[@]}"
elif (( "${#idleAcctArray[@]}" >= 1 ))&&(( "${#orphanArray[@]}" >= 1 )); then
printWarn "Idle_Accounts=${#idleAcctArray[@]} Idle accounts found" \
"${idleAcctArray[@]}" \
"Following orphan directories were also found:" \
"${orphanArray[@]}"
elif (( "${#idleAcctArray[@]}" == 0 ))&&(( "${#orphanArray[@]}" >= 1 )); then
printWarn "Idle_Accounts=${#idleAcctArray[@]} Orphan directories were found:" \
"${orphanArray[@]}" \
"No idle accounts found"
else
printOK "Idle_Accounts=${#idleAcctArray[@]} No idle accounts found"
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment