diff --git a/Readme.md b/Readme.md index 5983160..6a1c1f9 100644 --- a/Readme.md +++ b/Readme.md @@ -257,6 +257,66 @@ The script can be put into the crontab and run once a month, it prints to STDOUT 0 2 1 * * root /root/users/bin/check_last_login.sh | mail -s "User Account check: $(hostname)" ``` +For processable output there is a "csv" and "json" option to output the data in CSV or JSON format + +Example csv + +```csv +Hostname,Username,Main Group,SSH Group,Account Created Date,Account Age,Last Login Date,Last Login Age,Login Source,Never Logged In,Status +some.host,name_a,staff,sshallow,2021-11-18,764,,,,true,[!] Never logged in: account created 764 days ago +some.host,name_b,staff,sshallow,2021-11-15,767,2023-12-20 22:18:36,1,ssh,false,OK [ssh] +... +``` + +```json +{ + "Info": { + "Hostname": "host.name", + "Date": "2023-12-21 13:11:45", + "MaxAgeLogin": 90, + "MaxAgeCreate": 30 + }, + "Users": [ + { + "Username": "name_a", + "SshGroup": "sshallow", + "MainGroup": "staff", + "SubGroups": [], + "AccountCreatedDate": "2021-11-18", + "AccountAge": "764", + "LastLoginDate": "", + "LastLoginAge": "", + "LoginSource": "", + "NeverLoggedIn": true, + "Status": "[!] Never logged in: account created 764 days ago" + }, + { + "Username": "name_b", + "SshGroup": "sshallow", + "MainGroup": "staff", + "SubGroups": [ + "sudo", + "users", + "ikea", + "harley", + "aeon", + "primaham", + "nisshin", + "nissan", + "pfizer" + ], + "AccountCreatedDate": "2021-11-15", + "AccountAge": "767", + "LastLoginDate": "2023-12-20 22:18:36", + "LastLoginAge": "1", + "LoginSource": "ssh", + "NeverLoggedIn": false, + "Status": "OK [ssh]" + }, + ] +} +``` + ## Delete users `bin/delete_user.sh -t -b ...` diff --git a/bin/check_last_login.sh b/bin/check_last_login.sh index 14ab7ab..f380417 100755 --- a/bin/check_last_login.sh +++ b/bin/check_last_login.sh @@ -6,17 +6,21 @@ # base folder BASE_FOLDER=$(dirname $(readlink -f $0))"/"; # which groups holds the ssh allowed login users (outside of admin users) -ssh_groups=('sshforward' 'sshallow'); +ssh_groups=('sshforward' 'sshallow' 'sshreject'); ssh_reject_group='sshreject'; # date now for compare now=$(date +"%s"); # max age for last login or account create without login -max_age_login=60; +max_age_login=90; max_age_create=30; # one day in seconds day=86400; # delete account strings lock_accounts=""; +unlock_flag=0; +unlock_accounts=""; +# only needed for json output +first_run=1; # log base folder LOG="${BASE_FOLDER}/../log"; # auth log file user;date from collect_login_data script @@ -30,37 +34,108 @@ if [ ! -d "${LOG}" ]; then echo "log folder ${LOG} not found"; exit; fi; -LOG="${LOG}/check_ssh_user."$(date +"%F_%H%m%S")".log"; -exec &> >(tee -a "${LOG}"); -echo "[START] =============>"; -echo "Hostname : "$(hostname); -echo "Run date : "$(date +"%F %T"); -echo "Max age last login: ${max_age_login} days"; -echo "Max age no login : ${max_age_create} days"; +# option 1 in list +case "${1,,}" in + text) + OUTPUT_TARGET="text"; + ;; + json) + OUTPUT_TARGET="json"; + echo "{"; + ;; + csv) + OUTPUT_TARGET="csv"; + echo "Hostname,Username,Main Group,SSH Group,Account Created Date,Account Age,Last Login Date,Last Login Age,Never Logged In,Login Source,Status"; + ;; + *) + OUTPUT_TARGET="text"; + ;; +esac; + +if [ "${OUTPUT_TARGET}" == "text" ]; then + LOG="${LOG}/check_ssh_user."$(date +"%F_%H%m%S")".log"; + exec &> >(tee -a "${LOG}"); + echo "[START] =============>"; + echo "Hostname : "$(hostname); + echo "Run date : "$(date +"%F %T"); + echo "Max age last login: ${max_age_login} days"; + echo "Max age no login : ${max_age_create} days"; +elif [ "${OUTPUT_TARGET}" == "json" ]; then + echo '"Info": {' + echo '"Hostname": "'$(hostname)'",'; + echo '"Date": "'$(date +"%F %T")'",'; + echo '"MaxAgeLogin": '${max_age_login}','; + echo '"MaxAgeCreate": '${max_age_create}''; + echo '},' + echo '"Users": [' +fi; for ssh_group in ${ssh_groups[@]}; do -echo "--------------------->" -echo "Checking Group : ${ssh_group}"; + if [ "${OUTPUT_TARGET}" == "text" ]; then + echo "--------------------->" + if [ "${ssh_group}" == "${ssh_reject_group}" ]; then + echo "Showing current SSH Reject users:"; + unlock_flag=1 + else + echo "Checking Group : ${ssh_group}"; + fi; + fi; for username in $(cat /etc/group|grep "${ssh_group}:" | cut -d ":" -f 4 | sed -e 's/,/ /g'); do # check that user exists in passwd if ! id "${username}" &>/dev/null; then - echo "[!] User $username does not exists in /etc/passwd file"; + out_string="[!] User $username does not exists in /etc/passwd file"; + case "${OUTPUT_TARGET}" in + text) + echo "${out_string}"; + ;; + json) + echo "{"; + echo '"Username": "'${username}'",'; + echo '"SshGroup": "'${ssh_group}'",'; + echo '"MainGroup": "",'; + echo '"SubGroups": [],'; + echo '"AccountCreatedDate": "",'; + echo '"AccountAge": "",'; + echo '"LastLoginDate": "",'; + echo '"LastLoginAge": "",'; + echo '"LoginSource": "",'; + echo '"NeverLoggedIn": true,'; + echo '"Status": "'${out_string}'"'; + echo "}"; + ;; + csv) + printf "%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s\n" "$(hostname)" "${username}" "" "${ssh_group}" "" "" "" "" "true" "${out_string}" + ;; + esac; continue; fi; + # for json output, we need , between outputs + if [ "${OUTPUT_TARGET}" == "json" ] && [ $first_run -eq 0 ]; then + echo ","; + fi; + first_run=0; + # unlock lis of users + if [ $unlock_flag -eq 1 ]; then + unlock_accounts="${unlock_accounts} ${username}" + fi; account_age=0; lock_user=0; out_string=""; + # get the main group for this user + main_group=$(id -gn "${username}") + # get all the sub groups for this user, and remove the main groups + sub_groups=$(id -Gn "${username}" | sed -e "s/${main_group}//" | sed -e "s/${ssh_group}//") #echo "* Checking user ${username}"; # check user create time, if we have set it in comment - user_create_date=$(cat /etc/passwd | grep "${username}:" | cut -d ":" -f 5); + user_create_date_string=$(cat /etc/passwd | grep "${username}:" | cut -d ":" -f 5); # if empty try last password set time - if [ -z "${user_create_date}" ]; then + if [ -z "${user_create_date_string}" ]; then # user L 11/09/2020 0 99999 7 -1 - user_create_date=$(passwd -S ${username} | cut -d " " -f 3); + user_create_date_string=$(passwd -S ${username} | cut -d " " -f 3); fi; # last try is user home .bash_logout - if [ -z "${user_create_date}" ]; then + if [ -z "${user_create_date_string}" ]; then home_dir=$(cat /etc/passwd | grep "${username}:" | cut -d ":" -f 6)"/.bash_logout"; - user_create_date=$(stat -c %Z "${home_dir}"); + user_create_date_string=$(stat -c %Z "${home_dir}"); fi; # below only works if the user logged in, a lot of them are just file upload @@ -69,13 +144,28 @@ echo "Checking Group : ${ssh_group}"; # user pts/35 10.110.160.230 Wed Nov 2 09:40:35 +0900 2022 last_login_string=$(lastlog -u ${username} | sed 1d); search="Never logged in"; + never_logged_in="false"; found=""; + login_source=""; + last_login_date=""; + account_age=""; + last_login=""; # problem with running rep check in if if [ -f "${AUTH_LOG}" ]; then found=$(grep "${username};" "${AUTH_LOG}"); fi; + # always pre work account dates if they exist, but output only if text + if [ ! -z "${user_create_date_string}" ]; then + user_create_date=$(echo "${user_create_date_string}" | date +"%s" -f -); + # if all empty, we continue with only check if user has last login date + # else get days since creation + #account_age=$[ ($(date +"%s")-$(date -d "${user_create_date}" +"%s"))/24 ]; + account_age=$(awk '{printf("%.0f\n",($1-$2)/$3)}' <<<"${now} ${user_create_date} ${day}"); + user_create_date_out=$(echo "${user_create_date_string}" | date +"%F" -f -); + fi; if [ ! -z "${found}" ]; then - last_login_date=$(grep "${username};" "${AUTH_LOG}" | cut -d ";" -f 2 | date +"%s" -f -); + last_login_date_string=$(grep "${username};" "${AUTH_LOG}" | cut -d ";" -f 2); + last_login_date=$(echo "${last_login_date}" | date +"%s" -f -); last_login=$(awk '{printf("%.0f\n",($1-$2)/$3)}' <<<"${now} ${last_login_date} ${day}"); if [ ${last_login} -gt ${max_age_login} ]; then out_string="[!] last ssh log in ${last_login} days ago"; @@ -83,6 +173,9 @@ echo "Checking Group : ${ssh_group}"; else out_string="OK [ssh]"; fi; + login_source="ssh"; + # rewrite to Y-M-D, aka + last_login_date="${last_login_date_string}" elif [ ! -z "${last_login_string##*$search*}" ]; then # if we have "** Never logged in**" the user never logged in # find \w{3} \w{3} [\s\d]{2} \d{2}:\d{2}:\d{2} \+\d{4} \d{4} @@ -96,39 +189,82 @@ echo "Checking Group : ${ssh_group}"; else out_string="OK [lastlog]"; fi; + login_source="lastlog"; + last_login_date=$(echo "${last_login_string}" | awk '{for(i=4;i<=NF;++i)printf $i FS}' | date +"%F %T" -f -) elif [ ! -z "${user_create_date}" ]; then - user_create_date=$(echo "${user_create_date}" | date +"%s" -f -); - # if all empty, we continue with only check if user has last login date - # else get days since creation - #account_age=$[ ($(date +"%s")-$(date -d "${user_create_date}" +"%s"))/24 ]; - account_age=$(awk '{printf("%.0f\n",($1-$2)/$3)}' <<<"${now} ${user_create_date} ${day}"); if [ ${account_age} -gt ${max_age_create} ]; then - out_string="[!] Never logged in, account created ${account_age} days ago"; + out_string="[!] Never logged in: account created ${account_age} days ago"; lock_user=1; else - out_string="OK [first login]"; + out_string="OK [Never logged in]"; fi; + never_logged_in="true"; else out_string="[!!!] Never logged in and we have no create date"; + never_logged_in="true"; fi; # build delete output if [ ${lock_user} = 1 ]; then lock_accounts="${lock_accounts} ${username}" fi; - printf "* Checking user %-20s: %s\n" "${username}" "${out_string}"; + case "${OUTPUT_TARGET}" in + text) + printf "* Checking user %-20s (%-20s): %s\n" "${username}" "${main_group}" "${out_string}"; + ;; + json) + sub_groups_string="[" + sub_group_first=1 + for s_group in $sub_groups; do + if [ "${sub_group_first}" == 0 ]; then + sub_groups_string="${sub_groups_string},"; + fi; + sub_groups_string="${sub_groups_string}\"${s_group}\""; + sub_group_first=0; + done; + sub_groups_string="${sub_groups_string}]"; + echo "{"; + echo '"Username": "'${username}'",'; + echo '"SshGroup": "'${ssh_group}'",'; + echo '"MainGroup": "'${main_group}'",'; + echo '"SubGroups": '${sub_groups_string}','; + echo '"AccountCreatedDate": "'${user_create_date_out}'",'; + echo '"AccountAge": "'${account_age}'",'; + echo '"LastLoginDate": "'${last_login_date}'",'; + echo '"LastLoginAge": "'${last_login}'",'; + echo '"LoginSource": "'${login_source}'",'; + echo '"NeverLoggedIn": '${never_logged_in}','; + echo '"Status": "'${out_string}'"'; + echo "}"; + ;; + csv) + printf "%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s\n" "$(hostname)" "${username}" "${main_group}" "${ssh_group}" "${user_create_date_out}" "${account_age}" "${last_login_date}" "${last_login}" "${never_logged_in}" "${login_source}" "${out_string}" + ;; + esac; done; done; -echo "--------------------->" -echo "Showing current SSH Reject users:" -for user in $(cat /etc/group|grep "${ssh_reject_group}:" | cut -d ":" -f 4 | sed -e 's/,/ /g'); do - echo "${user}"; -done; -if [ ! -z "${lock_accounts}" ]; then - echo "--------------------->" - echo "% Run script below to move users to reject ssh group"; - echo ""; - echo "bin/lock_user.sh ${lock_accounts}"; +if [ "${OUTPUT_TARGET}" == "text" ]; then + if [ ! -z "${lock_accounts}" ]; then + echo "--------------------->" + echo "% Run script below to move users to reject ssh group"; + echo ""; + echo "bin/lock_user.sh ${lock_accounts}"; + fi; + if [ ! -z "${unlock_accounts}" ]; then + echo "--------------------->" + echo "% Run script below to move users to allow or forward ssh group"; + echo ""; + echo "For ALLOW:" + echo "bin/unlock_user.sh -s allow ${lock_accounts}"; + echo ""; + echo "For FORWARDONLY:" + echo "bin/unlock_user.sh -s forward ${lock_accounts}"; + fi; + echo "[END] ===============>" +elif [ "${OUTPUT_TARGET}" == "json" ]; then + # users + echo "]"; + # overall + echo "}"; fi; -echo "[END] ===============>" # __END__