Created
September 13, 2020 00:13
-
-
Save rawiriblundell/4ac56e23b253a89c8d65ec2d5579f52f to your computer and use it in GitHub Desktop.
checkmk local check for local account auditing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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