|
|
|
|
@@ -6,17 +6,22 @@
|
|
|
|
|
# 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;
|
|
|
|
|
warn_age_login=80;
|
|
|
|
|
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 +35,142 @@ 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";
|
|
|
|
|
if [ -z $(command -v curl) ]; then
|
|
|
|
|
echo "Missing curl application, aborting";
|
|
|
|
|
error=1;
|
|
|
|
|
fi;
|
|
|
|
|
if [ -z $(command -v jq) ]; then
|
|
|
|
|
echo "Missing jq application, aborting";
|
|
|
|
|
error=1;
|
|
|
|
|
fi;
|
|
|
|
|
# option 1 in list
|
|
|
|
|
case "${1,,}" in
|
|
|
|
|
text)
|
|
|
|
|
OUTPUT_TARGET="text";
|
|
|
|
|
;;
|
|
|
|
|
json)
|
|
|
|
|
OUTPUT_TARGET="json";
|
|
|
|
|
echo "{";
|
|
|
|
|
;;
|
|
|
|
|
csv)
|
|
|
|
|
CSV_LINE="%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s\n";
|
|
|
|
|
OUTPUT_TARGET="csv";
|
|
|
|
|
echo "Account ID,Region,Instance ID,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;
|
|
|
|
|
|
|
|
|
|
# collect info via: curl http://instance-data/latest/meta-data/
|
|
|
|
|
instance_id=$(curl -s http://instance-data/latest/meta-data/instance-id)
|
|
|
|
|
account_id=$(curl -s http://instance-data/latest/meta-data/identity-credentials/ec2/info/ | jq -r .AccountId)
|
|
|
|
|
region=$(curl -s http://instance-data/latest/meta-data/placement/region)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if [ "${OUTPUT_TARGET}" = "text" ]; then
|
|
|
|
|
LOG="${LOG}/check_ssh_user."$(date +"%F_%H%m%S")".log";
|
|
|
|
|
exec &> >(tee -a "${LOG}");
|
|
|
|
|
echo "[START] =============>";
|
|
|
|
|
echo "AWS ID : ${account_id}";
|
|
|
|
|
echo "Region : ${region}";
|
|
|
|
|
echo "Instance ID : ${instance_id}";
|
|
|
|
|
echo "Hostname : "$(hostname);
|
|
|
|
|
echo "Run date : "$(date +"%F %T");
|
|
|
|
|
echo "Max age last login : ${max_age_login} days";
|
|
|
|
|
echo "Warn age last login: ${warn_age_login} days";
|
|
|
|
|
echo "Max age no login : ${max_age_create} days";
|
|
|
|
|
elif [ "${OUTPUT_TARGET}" = "json" ]; then
|
|
|
|
|
echo '"Info": {'
|
|
|
|
|
echo '"AccountId": "'${account_id}'",';
|
|
|
|
|
echo '"Region": "'${region}'",';
|
|
|
|
|
echo '"InstanceId": "'${instance_id}'",';
|
|
|
|
|
echo '"Hostname": "'$(hostname)'",';
|
|
|
|
|
echo '"Date": "'$(date +"%F %T")'",';
|
|
|
|
|
echo '"MaxAgeLogin": '${max_age_login}',';
|
|
|
|
|
echo '"WarnAgeLogin": '${warn_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 "${CSV_LINE}" "${account_id}" "${region}" "${instance_id}" "$(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 ! [[ "${user_create_date_string}" =~ ^\d{4}-\d{2}-\{2} ]]; 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
|
|
|
|
|
home_dir=$(cat /etc/passwd | grep "${username}:" | cut -d ":" -f 6)"/.bash_logout";
|
|
|
|
|
user_create_date=$(stat -c %Z "${home_dir}");
|
|
|
|
|
if ! [[ "${user_create_date_string}" =~ ^\d{4}-\d{2}-\{2} ]]; then
|
|
|
|
|
# try logout or bash history
|
|
|
|
|
home_dir_bl=$(cat /etc/passwd | grep "${username}:" | cut -d ":" -f 6)"/.bash_logout";
|
|
|
|
|
home_dir_bh=$(cat /etc/passwd | grep "${username}:" | cut -d ":" -f 6)"/.bash_history";
|
|
|
|
|
# check that this file exists
|
|
|
|
|
if [ -f "${home_dir_bl}" ]; then
|
|
|
|
|
user_create_date_string=$(stat -c %Z "${home_dir_bl}");
|
|
|
|
|
elif [ -f "${home_dir_bh}" ]; then
|
|
|
|
|
user_create_date_string=$(stat -c %Z "${home_dir_bh}");
|
|
|
|
|
fi;
|
|
|
|
|
fi;
|
|
|
|
|
# still no date -> set empty
|
|
|
|
|
if ! [[ "${user_create_date_string}" =~ ^\d{4}-\d{2}-\{2} ]]; then
|
|
|
|
|
user_create_date_string="";
|
|
|
|
|
fi;
|
|
|
|
|
|
|
|
|
|
# below only works if the user logged in, a lot of them are just file upload
|
|
|
|
|
@@ -69,20 +179,42 @@ 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";
|
|
|
|
|
lock_user=1;
|
|
|
|
|
if [ "${ssh_group}" != "${ssh_reject_group}" ]; then
|
|
|
|
|
lock_user=1;
|
|
|
|
|
fi;
|
|
|
|
|
elif [ ${last_login} -gt ${warn_age_login} ]; then
|
|
|
|
|
out_string="OK [last ssh login ${last_login} days ago]";
|
|
|
|
|
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}
|
|
|
|
|
@@ -92,43 +224,92 @@ echo "Checking Group : ${ssh_group}";
|
|
|
|
|
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 terminal log in ${last_login} days ago";
|
|
|
|
|
lock_user=1;
|
|
|
|
|
if [ "${ssh_group}" != "${ssh_reject_group}" ]; then
|
|
|
|
|
lock_user=1;
|
|
|
|
|
fi;
|
|
|
|
|
elif [ ${last_login} -gt ${warn_age_login} ]; then
|
|
|
|
|
out_string="OK [last terminal login ${last_login} days ago]";
|
|
|
|
|
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";
|
|
|
|
|
lock_user=1;
|
|
|
|
|
out_string="[!] Never logged in: account created ${account_age} days ago";
|
|
|
|
|
if [ "${ssh_group}" != "${ssh_reject_group}" ]; then
|
|
|
|
|
lock_user=1;
|
|
|
|
|
fi;
|
|
|
|
|
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
|
|
|
|
|
if [ ${lock_user} -eq 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 "${CSV_LINE}" "${account_id}" "${region}" "${instance_id}" "$(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 ${unlock_accounts}";
|
|
|
|
|
echo "";
|
|
|
|
|
echo "For FORWARDONLY:"
|
|
|
|
|
echo "bin/unlock_user.sh -s forward ${unlock_accounts}";
|
|
|
|
|
fi;
|
|
|
|
|
echo "[END] ===============>"
|
|
|
|
|
elif [ "${OUTPUT_TARGET}" = "json" ]; then
|
|
|
|
|
# users
|
|
|
|
|
echo "]";
|
|
|
|
|
# overall
|
|
|
|
|
echo "}";
|
|
|
|
|
fi;
|
|
|
|
|
echo "[END] ===============>"
|
|
|
|
|
|
|
|
|
|
# __END__
|
|
|
|
|
|