Compare commits

..

32 Commits

Author SHA1 Message Date
Clemens Schwaighofer
fa47178ed1 Add central logging for all actions done
log file "user_management.log"

Each line is
[YYYY-MM-DD HH:mm:ss] [script name] [TEST] ...

[TEST] is only set if we are in a test run

for create user, if info flag is set, we do not write a log
2024-12-09 11:37:37 +09:00
Clemens Schwaighofer
4629b58a7e Skip empty group on login check 2024-11-11 17:06:24 +09:00
Clemens Schwaighofer
d8cd628ddd Fix for check last login script
the data reading was split with " " (space) which in the while read kept
it as one row, changed the split character to "\n"
2024-10-24 13:57:04 +09:00
Clemens Schwaighofer
76f9056069 Fix user create with groups
Move the params into array that we add as dynamic parameters to call
2024-10-11 14:39:56 +09:00
Clemens Schwaighofer
74e7e9823f Bug fix for user create group part 2024-10-11 09:25:41 +09:00
Clemens Schwaighofer
7ce8330aa6 shellcheck fixup 2024-09-27 18:49:36 +09:00
Clemens Schwaighofer
d8f9710593 Merge branch 'development' into shellecheck-cleanup 2024-09-24 11:40:13 +09:00
Clemens Schwaighofer
c37d17fe9f ReadMe file update 2024-09-24 11:38:45 +09:00
Clemens Schwaighofer
307433e0a7 Merge branch 'development' into shellecheck-cleanup 2024-09-20 09:22:20 +09:00
Clemens Schwaighofer
f9cd0806f1 Fix in Readme file for exmaple user_list.txt 2024-09-20 09:22:02 +09:00
Clemens Schwaighofer
d9b3fd97fb Merge branch 'development' into shellecheck-cleanup 2024-09-20 09:20:36 +09:00
Clemens Schwaighofer
21177e1762 Updates for ReadMe file for no login shell settings 2024-09-20 09:20:01 +09:00
Clemens Schwaighofer
3aa6de7e23 check last login shellcheck fixes 2024-09-06 14:44:34 +09:00
Clemens Schwaighofer
3c160a62e7 Merge branch 'development' into shellecheck-cleanup 2024-09-06 14:37:37 +09:00
Clemens Schwaighofer
ca4616c5ee Text fix for error strings in last login check 2024-09-06 14:36:09 +09:00
Clemens Schwaighofer
251b0bf981 SSH login last date check was wrong 2024-09-06 14:34:38 +09:00
Clemens Schwaighofer
6daccfe57c Check last login update with more days info
if WARN then write WARN instead of OK.
Add day numbers if OK

Some minor shellscript updates
2024-09-06 14:25:28 +09:00
Clemens Schwaighofer
91f096ab0c Merge branch 'master' into shellecheck-cleanup 2024-09-06 10:58:13 +09:00
Clemens Schwaighofer
935d6a84c9 Add login shell type select (bash login or no login), fix ssh base groups
no ssh allow/forward/reject base group was set if an optional sub group was set

Add possibility to chose no login when setting the ssh access type to "...|no_login"
2024-09-06 10:44:31 +09:00
Clemens Schwaighofer
ee7cc8555e Merge branch 'development' into shellecheck-cleanup 2024-09-04 14:05:51 +09:00
Clemens Schwaighofer
83f84abd46 Fix skip naming for zip/move creation 2024-09-04 14:04:25 +09:00
Clemens Schwaighofer
090d6f9cec Download zip file message only if there is a file to download 2024-09-04 13:44:47 +09:00
Clemens Schwaighofer
5659cc010f Update zip file/clean up skip with skip information 2024-09-04 13:43:24 +09:00
Clemens Schwaighofer
0bd40cdd73 Create user: skip zip creation run if there are no PEM files
Avoid "file not found" zip file creation and remove if there are no
PEM files created, eg if we have a pre defined pub file
2024-09-04 13:21:36 +09:00
Clemens Schwaighofer
e4ed6fed8d shellcheck based code cleanup 2024-09-04 11:16:05 +09:00
Clemens Schwaighofer
5bf30a8b2f Add shellcheckrc 2024-09-03 12:47:52 +09:00
Clemens Schwaighofer
26c007dba6 Remove error=1 debug set 2024-02-15 18:10:53 +09:00
Clemens Schwaighofer
785e3c116d Fix password set length check 2024-02-15 18:07:53 +09:00
Clemens Schwaighofer
adbfeb0074 Password length set, auto set if not set, and set for pwgen actually 2024-02-15 18:05:03 +09:00
Clemens Schwaighofer
8c7ef32894 Create user script update: allow no password, allow different password length 2024-02-15 18:03:16 +09:00
Clemens Schwaighofer
70ef7a3fc5 Check last login mandatory settings now has combined abort
set an error flag and check all settings before exit program
2024-02-14 14:46:17 +09:00
Clemens Schwaighofer
89252af50b Bug fix for json export with double quotes 2023-12-22 13:39:18 +09:00
11 changed files with 565 additions and 281 deletions

2
.shellcheckrc Normal file
View File

@@ -0,0 +1,2 @@
shell=bash
external-sources=true

View File

@@ -1,4 +1,4 @@
# AWS User Creation
# User creation on AWS Servers
Two files to create new user entries with an SSH key and zip all the data for download
@@ -23,8 +23,41 @@ Alternate download:
Inside the base folder there are
- ssh-keygen for temporary holding the PEM/PUB files
- zip file which holds the created user list, password and PEM/PUB files
- auth-log/: access logs from users for the last login check
- bin/: scripts folder
- backup/: for removed users home directories
- config/: config settings (eg alternative home base folder
- log/: user creation and other action logs
- ssh-keygen/: PEM keys are stored here during creation and then zip-ed into the zip folder
- ssh-keygen-created-pub/: Public keys are stored here and stay here (used for checking)
- user_list.txt: users that should exist on this server with various settings
- [user_password.YYYYMMDD-HHmmss.txt] temporary file with username and password, zip-ed into the zip folder
- zip/: after user creation the password and PEM files are stored in here
## Config
### create_user.sh: create_user.cfg
A `create_user.cfg` can be created to set a differen HOME_LOCATION and PASSWORD_LENGTH values
eg:
```ini
HOME_LOCATION="/storage"
PASSWORD_LENGTH=14
```
### authorized_key_location_change.sh: authorized_key_location_change.ignore
For this script a `authorized_key_location_change.ignore` with a list of user names to ignore for the
move
eg:
```ini
foo_user
bar_user
```
## Options
@@ -47,12 +80,12 @@ In the `/root/users/` folder there needs to be a file called '*user_list.txt*'
This is a CSV type file with the following layout
ID | Username | Group and Sub Group | SSH Access Type | Optional Password | Override host name | Override ssh key type
ID | Username | Group and Sub Group | SSH Access Type and No Login flag | Optional Password | Override host name | Override ssh key type
-|-|-|-|-|-|-
1: The ID, Username and Group column must be filled.
2: For sub groups add them with a *,* The first group is the master group
3: SSH Access type as: allow/forward. allow is default
2: For sub groups add them with a MASTER_GROUP,SUB_GROUP_A,SUB_GROUP_B,... The first group is the master group
3: SSH Access type as: allow/forward. allow is default, additional separated by | can be a "no_login" to set a no login shell, else bash shell will be set
4: If the password column is filled, the string from here will be used as the PEM Key password.
5: If a override hostname is set it will be used instead of `hostname`
6: If the ssh key type is set, it will override the default *ed25519* type. Only *rsa* is allowed. This is for setting up backwards compatible lists. Change is not recommended
@@ -65,11 +98,12 @@ The file can hold comments. The first character in the line must be a *#*
Example file
```csv
#user_id;user_name;group,subgroup;ssh access type;override password;override hostname;override ssh type
#user_id;user_name;group,subgroup;ssh access type|no login flag;override password;override hostname;override ssh type
user1;some.name;group-a;allow;;hostname;
user2;othername;group-a;allow;;;
# I am a comment
;username;groupC;allow;setpassword;;
;username_c;groupC;allow|no_login;;;
...
```

View File

@@ -1,5 +1,7 @@
#!/usr/bin/env bash
# shellcheck disable=SC2059
# check if we need to move the users authorized keys to the central location
TEST=1;
@@ -7,14 +9,14 @@ LIST=0;
SKIP_USERS=();
while getopts ":gls:" opt; do
case "${opt}" in
g|go)
g) # go
# default we test
TEST=0;
;;
s|skip)
s) # skip
SKIP_USERS+=("${OPTARG}");
;;
l|list)
l) # list
LIST=1;
;;
\?)
@@ -29,10 +31,11 @@ done;
# detect ssh authorized_keys setting
SSH_CENTRAL_AUTHORIZED_FILE_FOLDER='';
SSH_MASTER_AUTHORIZED_FILE='';
SSH_AUTHORIZED_FILE='';
# SSH_AUTHORIZED_FILE='';
# shellcheck disable=SC2013
for cf in $(grep "^AuthorizedKeysFile" /etc/ssh/sshd_config | grep "%u"); do
if [ ! -z $(echo "${cf}" | grep "%u") ]; then
SSH_CENTRAL_AUTHORIZED_FILE_FOLDER=$(echo "${cf}" | sed -e 's/%u//');
if echo "$cf" | grep -q "%u"; then
SSH_CENTRAL_AUTHORIZED_FILE_FOLDER="${cf/%%u//}";
if [ ! -d "${SSH_CENTRAL_AUTHORIZED_FILE_FOLDER}" ]; then
echo "ssh central authorized_file folder could not be found: ${SSH_CENTRAL_AUTHORIZED_FILE_FOLDER}";
exit;
@@ -43,8 +46,9 @@ if [ -z "${SSH_CENTRAL_AUTHORIZED_FILE_FOLDER}" ]; then
echo "No central authorized_keys file detected, no change check needed";
exit;
fi;
# shellcheck disable=SC2013
for cf in $(grep "^AuthorizedKeysFile" /etc/ssh/sshd_config | grep -- "--master"); do
if [ ! -z $(echo "${cf}" | grep -- "--master") ]; then
if ! echo "${cf}" | grep -q -- "--master"; then
SSH_MASTER_AUTHORIZED_FILE="${cf}";
if [ ! -f "${SSH_MASTER_AUTHORIZED_FILE}" ]; then
echo "ssh master authorized_file could not be found: ${SSH_MASTER_AUTHORIZED_FILE}"l
@@ -66,7 +70,7 @@ if [ ${LIST} -eq 1 ]; then
fi;
# base folder
BASE_FOLDER=$(dirname $(readlink -f $0))"/";
BASE_FOLDER=$(dirname "$(readlink -f "$0")")"/";
# output printf
PRINTF_INFO="%-8s [%3s]: %-25s: %s\n";
# list of user accounts we will never touch
@@ -84,23 +88,44 @@ if [ -f "${BASE_FOLDER}${IGNORE_USER_FILE}" ]; then
echo "Reading ${IGNORE_USER_FILE}";
fi;
LOG="${BASE_FOLDER}/../log/user_management.log";
function write_log()
{
text="${1}";
do_echo="${2}";
log_prefix="";
# log prefix for testing
if [ ${TEST} -eq 1 ]; then
log_prefix="[TEST] ";
fi;
# write log not in info run
echo "[$(date +"%F %T")] [$0] ${log_prefix}${text}" >> "${LOG}";
if [ "${do_echo}" = "1" ]; then
echo "${text}";
fi;
}
write_log "START SCRIPT RUN";
# loop over passwd file
# if not in no action then check if .ssh/authorized_keys file exists
cat /etc/passwd | cut -d ":" -f 1,6 |
while read user_home; do
cut -d ":" -f 1,6 /etc/passwd |
while read -r user_home; do
username=$(echo "${user_home}" | cut -d ":" -f 1);
master_user=0;
# skip admin usernames
if [[ " ${NO_ACTION[*]} " =~ " ${username} " ]]; then
if [[ " ${NO_ACTION[*]} " =~ [[:space:]]${username}[[:space:]] ]]; then
printf "${PRINTF_INFO}" "NO ACT" "!" "${username}" "user in NO ACTION list";
write_log "[NO ACT] ${username} in NO ACTION list";
continue;
fi;
if [[ " ${SKIP_USERS[*]} " =~ " ${username} " ]]; then
if [[ " ${SKIP_USERS[*]} " =~ [[:space:]]${username}[[:space:]] ]]; then
printf "${PRINTF_INFO}" "SKIP" "*" "${username}" "skip forced via command line";
write_log "[SKIP] ${username} skip forced via command line";
continue;
fi;
if [[ " ${IGNORE_USER[*]} " =~ " ${username} " ]]; then
if [[ " ${IGNORE_USER[*]} " =~ [[:space:]]${username}[[:space:]] ]]; then
printf "${PRINTF_INFO}" "SKIP" "**" "${username}" "skip from ignore config file";
write_log "[SKIP] ${username} skip from ignore config file";
continue;
fi;
home_folder=$(echo "${user_home}" | cut -d ":" -f 2);
@@ -109,17 +134,20 @@ while read user_home; do
# but do we have an auth folder, if yes -> exist skip
if [ -f "${SSH_CENTRAL_AUTHORIZED_FILE_FOLDER}/${username}" ]; then
printf "${PRINTF_INFO}" "DONE" "." "${username}" "already moved";
write_log "[DONE] ${username} already moved";
else
printf "${PRINTF_INFO}" "IGNORE" "?" "${username}" "no authorized_keys file";
write_log "[IGNORE] ${username} no authorized_keys file";
fi;
continue;
fi;
# check those keys are in the master key list
if [[ " ${MASTER_KEY[*]} " =~ " ${username} " ]]; then
if [[ " ${MASTER_KEY[*]} " =~ [[:space:]]${username}[[:space:]] ]]; then
master_user=1;
ssh_key_diff=$(diff -u "${home_folder}/.ssh/authorized_keys" "${SSH_MASTER_AUTHORIZED_FILE}");
if [ ! -z "${ssh_key_diff}" ]; then
if [ -n "${ssh_key_diff}" ]; then
printf "${PRINTF_INFO}" "ABORT" "!!!" "${username}" "authorized key is not matching the master key file";
write_log "[ABORT] ${username} authorized key is not matching the master key file";
exit;
fi;
fi;
@@ -128,6 +156,7 @@ while read user_home; do
ssh_key_diff=$(diff -u "${home_folder}/.ssh/authorized_keys" "${SSH_CENTRAL_AUTHORIZED_FILE_FOLDER}/${username}");
if [ -z "${ssh_key_diff}" ]; then
printf "${PRINTF_INFO}" "REMOVE" "-" "${username}" ".ssh/authorized_keys";
write_log "[REMOVE] ${username} .ssh/authorized_keys";
if [ ${master_user} -eq 0 ]; then
if [ ${TEST} -eq 0 ]; then
rm "${home_folder}/.ssh/authorized_keys";
@@ -135,33 +164,36 @@ while read user_home; do
echo "$> rm \"${home_folder}/.ssh/authorized_keys\"";
fi;
else
echo "[!] No delete for master user, must be done manually";
write_log "[!] No delete for master user, must be done manually" "1";
fi;
continue;
fi;
# No update, alert
printf "${PRINTF_INFO}" "DIFF" "???" "${username}" "Different authorized keys in home dir, SKIPPED";
write_log "[DIFF] ${username} Different authorized keys in home dir, SKIPPED";
continue;
fi;
printf "${PRINTF_INFO}" "MOVE" ">" "${username}" "Move SSH Key to central location";
write_log "[MOVE] ${username} Move SSH Key to central location";
# move public keys over
if [ ${TEST} -eq 0 ]; then
cat "${home_folder}/.ssh/authorized_keys" > "${SSH_CENTRAL_AUTHORIZED_FILE_FOLDER}/${username}";
# secure new folder: chown/chmod/chattr
chown ${username} "${SSH_CENTRAL_AUTHORIZED_FILE_FOLDER}/${username}";
chown "${username}" "${SSH_CENTRAL_AUTHORIZED_FILE_FOLDER}/${username}";
chmod 400 "${SSH_CENTRAL_AUTHORIZED_FILE_FOLDER}/${username}";
chattr +i "${SSH_CENTRAL_AUTHORIZED_FILE_FOLDER}/${username}";
# confirm
ssh_key_diff=$(diff -u "${home_folder}/.ssh/authorized_keys" "${SSH_CENTRAL_AUTHORIZED_FILE_FOLDER}/${username}");
if [ ! -z "${ssh_key_diff}" ]; then
if [ -n "${ssh_key_diff}" ]; then
printf "${PRINTF_INFO}" "ERROR" "!!!" "${username}" "Move problem ${ssh_key_diff}";
write_log "[ERROR] ${username} Move problem ${ssh_key_diff}";
break;
fi;
# remove home .ssh/authorized_keys (do not remove folder)
if [ ${master_user} -eq 0 ]; then
rm "${home_folder}/.ssh/authorized_keys";
else
echo "=> No delete for master user, must be done manually";
write_log "=> No delete for master user, must be done manually" "1";
fi;
else
echo "[START] ====>";

View File

@@ -4,7 +4,7 @@
# if user login >30days, remoe user from sshallow group and write log
# base folder
BASE_FOLDER=$(dirname $(readlink -f $0))"/";
BASE_FOLDER=$(dirname "$(readlink -f "$0")")"/";
# which groups holds the ssh allowed login users (outside of admin users)
ssh_groups=('sshforward' 'sshallow' 'sshreject');
ssh_reject_group='sshreject';
@@ -27,22 +27,26 @@ LOG="${BASE_FOLDER}/../log";
# auth log file user;date from collect_login_data script
AUTH_LOG="${BASE_FOLDER}/../auth-log/user_auth.log";
if [ $(whoami) != "root" ]; then
error=0;
if [ "$(whoami)" != "root" ]; then
echo "Script must be run as root user";
exit;
error=1;
fi;
if [ ! -d "${LOG}" ]; then
echo "log folder ${LOG} not found";
exit;
error=1;
fi;
if [ -z $(command -v curl) ]; then
if [ -z "$(command -v curl)" ]; then
echo "Missing curl application, aborting";
error=1;
fi;
if [ -z $(command -v jq) ]; then
if [ -z "$(command -v jq)" ]; then
echo "Missing jq application, aborting";
error=1;
fi;
if [ $error -eq 1 ]; then
exit;
fi;
# option 1 in list
case "${1,,}" in
text)
@@ -64,7 +68,7 @@ esac;
# collect info via: curl http://169.254.169.254/latest/meta-data/
instance_data=$(
TOKEN=`curl -s -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600"` &&
TOKEN=$(curl -s -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600") &&
curl -s -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/dynamic/instance-identity/document
)
instance_id=$(echo "${instance_data}" | jq .instanceId)
@@ -72,31 +76,31 @@ account_id=$(echo "${instance_data}" | jq .accountId)
region=$(echo "${instance_data}" | jq .region)
if [ "${OUTPUT_TARGET}" = "text" ]; then
LOG="${LOG}/check_ssh_user."$(date +"%F_%H%m%S")".log";
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 "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 '"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
for ssh_group in "${ssh_groups[@]}"; do
if [ "${OUTPUT_TARGET}" = "text" ]; then
echo "--------------------->"
if [ "${ssh_group}" = "${ssh_reject_group}" ]; then
@@ -106,7 +110,11 @@ for ssh_group in ${ssh_groups[@]}; do
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
while read -r username; do
# skip empty, if group exists but has no users
if [ "${username}" = "" ]; then
continue;
fi;
# check that user exists in passwd
if ! id "${username}" &>/dev/null; then
out_string="[!] User $username does not exists in /etc/passwd file";
@@ -116,8 +124,8 @@ for ssh_group in ${ssh_groups[@]}; do
;;
json)
echo "{";
echo '"Username": "'${username}'",';
echo '"SshGroup": "'${ssh_group}'",';
echo '"Username": "'"${username}"'",';
echo '"SshGroup": "'"${ssh_group}"'",';
echo '"MainGroup": "",';
echo '"SubGroups": [],';
echo '"AccountCreatedDate": "",';
@@ -126,10 +134,11 @@ for ssh_group in ${ssh_groups[@]}; do
echo '"LastLoginAge": "",';
echo '"LoginSource": "",';
echo '"NeverLoggedIn": true,';
echo '"Status": "'${out_string}'"';
echo '"Status": "'"${out_string}"'"';
echo "}";
;;
csv)
# shellcheck disable=SC2059
printf "${CSV_LINE}" "${account_id}" "${region}" "${instance_id}" "$(hostname)" "${username}" "" "${ssh_group}" "" "" "" "" "true" "${out_string}"
;;
esac;
@@ -153,17 +162,17 @@ for ssh_group in ${ssh_groups[@]}; do
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_string=$(cat /etc/passwd | grep "${username}:" | cut -d ":" -f 5);
user_create_date_string=$(grep "${username}:" /etc/passwd | cut -d ":" -f 5);
# if empty try last password set time
if ! [[ "${user_create_date_string}" =~ ^\d{4}-\d{2}-\{2} ]]; then
# user L 11/09/2020 0 99999 7 -1
user_create_date_string=$(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 ! [[ "${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";
home_dir_bl=$(grep "${username}:" /etc/passwd | cut -d ":" -f 6)"/.bash_logout";
home_dir_bh=$(grep "${username}:" /etc/passwd | 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}");
@@ -180,7 +189,7 @@ for ssh_group in ${ssh_groups[@]}; do
# users. Use the collect script from systemd-logind or /var/log/secure
# Username Port From Latest
# user pts/35 10.110.160.230 Wed Nov 2 09:40:35 +0900 2022
last_login_string=$(lastlog -u ${username} | sed 1d);
last_login_string=$(lastlog -u "${username}" | sed 1d);
search="Never logged in";
never_logged_in="false";
found="";
@@ -193,7 +202,7 @@ for ssh_group in ${ssh_groups[@]}; do
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
if [ -n "${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
@@ -201,50 +210,50 @@ for ssh_group in ${ssh_groups[@]}; do
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
if [ -n "${found}" ]; then
last_login_date_string=$(grep "${username};" "${AUTH_LOG}" | cut -d ";" -f 2);
last_login_date=$(echo "${last_login_date}" | date +"%s" -f -);
last_login_date=$(echo "${last_login_date_string}" | 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";
if [ "${last_login}" -gt ${max_age_login} ]; then
out_string="[!] Last ssh log in ${last_login} days ago";
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]";
elif [ "${last_login}" -gt ${warn_age_login} ]; then
out_string="WARN [last ssh login ${last_login} days ago]";
else
out_string="OK [ssh]";
out_string="OK [ssh, ${last_login} days ago]";
fi;
login_source="ssh";
# rewrite to Y-M-D, aka
last_login_date="${last_login_date_string}"
elif [ ! -z "${last_login_string##*$search*}" ]; then
elif [ -n "${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}
# awk '{for(i=4;i<=NF;++i)printf $i FS}'
last_login_date=$(echo "${last_login_string}" | awk '{for(i=4;i<=NF;++i)printf $i FS}' | date +"%s" -f -);
# date -d "Wed Nov 2 09:40:35 +0900 2022" +%s
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";
if [ "${last_login}" -gt ${max_age_login} ]; then
out_string="[!] Last terminal log in ${last_login} days ago";
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]";
elif [ "${last_login}" -gt ${warn_age_login} ]; then
out_string="WARN [last terminal login ${last_login} days ago]";
else
out_string="OK [lastlog]";
out_string="OK [lastlog, ${last_login} days ago]";
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
if [ ${account_age} -gt ${max_age_create} ]; then
elif [ -n "${user_create_date}" ]; then
if [ "${account_age}" -gt ${max_age_create} ]; then
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 [Never logged in]";
out_string="OK [Never logged in, created ${account_age} days ago]";
fi;
never_logged_in="true";
else
@@ -271,33 +280,34 @@ for ssh_group in ${ssh_groups[@]}; do
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 '"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)
# shellcheck disable=SC2059
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 <<< "$(grep "${ssh_group}:" /etc/group | cut -d ":" -f 4 | sed -e 's/,/\n/g')";
done;
if [ "${OUTPUT_TARGET}" = "text" ]; then
if [ ! -z "${lock_accounts}" ]; then
if [ -n "${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
if [ -n "${unlock_accounts}" ]; then
echo "--------------------->"
echo "% Run script below to move users to allow or forward ssh group";
echo "";

View File

@@ -9,7 +9,7 @@ if [[ "$EUID" -ne "0" ]]; then
fi;
# base folder
BASE_FOLDER=$(dirname $(readlink -f $0))"/";
BASE_FOLDER=$(dirname "$(readlink -f "$0")")"/";
# auth log file
AUTH_LOG="${BASE_FOLDER}/../auth-log/user_auth.log";
if [ ! -f "${AUTH_LOG}" ]; then
@@ -22,13 +22,19 @@ RUN_FULL_LOG=0;
# option parsing
while getopts ":fd" opt; do
case "${opt}" in
f|full)
f) # full
echo "[!!!] Run through all log files to collect data";
RUN_FULL_LOG=1;
;;
d|deubg)
d) # deubg
DEBUG=1;
;;
\?)
echo "";
echo "-f Collect all log data again";
echo "-d Debug output";
exit 1;
;;
esac;
done;
@@ -37,8 +43,8 @@ function prD()
message="${1}";
debug=${2:-0};
lb_off=${3:-0};
if [ ${debug} -eq 1 ]; then
if [ ${lb_off} -eq 1 ]; then
if [ "${debug}" -eq 1 ]; then
if [ "${lb_off}" -eq 1 ]; then
echo -n "${message}";
else
echo "${message}";
@@ -72,25 +78,26 @@ function parseLog()
# $(printf "USER: %-20s: %19s" "${auth_user}" "${auth_date}")
# prD "USER: $auth_user | DATE: $auth_date" ${debug} 1;
printf -v msg "Source: %-10s | Year: %4s | Last auth user: %-20s: %19s" "${logger}" "${start_year}" "${auth_user}" "${auth_date}"
prD "${msg}" ${debug} 1;
prD "${msg}" "${debug}" 1;
# find auth user in current auth file
# if not there attach, else replace date only
found=$(grep "${auth_user};" "${auth_log}");
if [ -z "${found}" ]; then
prD " | Write new" ${debug};
prD " | Write new" "${debug}";
echo "${auth_user};${auth_date}" >> "${auth_log}";
else
prD " | Replace old" ${debug};
prD " | Replace old" "${debug}";
sed -i "s/${auth_user};.*$/${auth_user};${auth_date}/" "${auth_log}";
fi;
}
printf -v msg "Run date: %s %s" $(date +"%F %T")
printf -v msg "Run date: %s" "$(date +"%F %T")"
prD "${msg}" ${DEBUG};
# Collector script for login information via journalctl
# if no systemd installed, try to get info from /var/log/secure or /var/log/auth.log
readonly init_version=$(/proc/1/exe --version | head -n 1);
init_version=$(/proc/1/exe --version | head -n 1);
readonly init_version;
if [ -z "${init_version##*systemd*}" ]; then
LOG_TARGET="systemd";
# for journalctl
@@ -103,8 +110,8 @@ if [ -z "${init_version##*systemd*}" ]; then
fi;
# READ as other format so we get the YEAR -o short-iso
START_YEAR=$(date +%Y -d "1 day ago");
journalctl -u systemd-logind --no-pager -o short-iso ${OPT_START_DATE} ${OPT_END_DATE} | grep ": New session" |
while read line; do
journalctl -u systemd-logind --no-pager -o short-iso "${OPT_START_DATE}" "${OPT_END_DATE}" | grep ": New session" |
while read -r line; do
# # Nov 21 14:15:46 we.are.hostname.com systemd-logind[1865]: New session 12345 of user some^user.
# date: 5 chars
# time: 8 chars
@@ -120,11 +127,11 @@ else
# for secure/auth log
if [ $RUN_FULL_LOG -eq 1 ]; then
# we loop over EACH file and get the DATE so we can have the correct YEAR
for sfile in $(ls -1 /var/log/secure*bz2); do
for sfile in /var/log/secure*bz2; do
tz=$(stat -c %Z "${sfile}");
START_YEAR=$(date +%Y -d @${tz});
START_YEAR=$(date +%Y -d @"${tz}");
bunzip2 -ck "${sfile}" | grep ": session opened for user" | grep " by (uid=0)" |
while read line; do
while read -r line; do
parseLog "${line}" "${AUTH_LOG}" "${START_YEAR}" "${LOG_TARGET}" ${DEBUG};
done;
done;
@@ -132,8 +139,8 @@ else
START_DATE="sshd"
fi;
START_YEAR=$(date +%Y -d "1 day ago");
cat /var/log/secure | grep "${START_DATE}" | grep ": session opened for user" | grep " by (uid=0)" |
while read line; do
grep "${START_DATE}" "/var/log/secure" | grep ": session opened for user" | grep " by (uid=0)" |
while read -r line; do
parseLog "${line}" "${AUTH_LOG}" "${START_YEAR}" "${LOG_TARGET}" ${DEBUG};
done;
fi;

View File

@@ -2,13 +2,14 @@
# * input file
# user_list.txt
# <ignored id>;<user name>;<group>[,sub group,sub group];<ssh access type>;[override password];[override hostname];[override ssh key type]
# <ignored id>;<user name>;<group>[,sub group,sub group];<ssh access type>|<no login flag>;[override password];[override hostname];[override ssh key type]
# lines with # are skipped
# already created users are skipped
# Mandatory: <ignored id>;<user name>;<group>;<ssh access type>
# <ssh access type> can be
# allow (full login access)
# forward (forward/jump host only)
# if in this column with pipe (|) the flag "no_login" is set then the default shell will change to "/sbin/nologin"
# * output file
# <date>;<target connect host name>;<hostname>;<username>;<password>;<ssh access type>
# If already existing PEM key is used then <password> is [ALREADY SET]
@@ -30,16 +31,16 @@ INFO=0; # no creation of anything, just print info strings
GO=0; # without this flag the script will exit with an info box
while getopts ":gtih:" opt; do
case "${opt}" in
g|go)
g) # go
GO=1;
;;
t|test)
t) # test
TEST=1;
;;
i|info)
i) # info
INFO=1;
;;
h|home)
h) # home
HOME_LOCATION="${OPTARG}";
;;
\?)
@@ -58,7 +59,7 @@ timestamp=$(date +%Y%m%d-%H%M%S)
# character to set getween info blocks
separator="#";
# base folder for all data
BASE_FOLDER=$(dirname $(readlink -f $0))"/";
BASE_FOLDER=$(dirname "$(readlink -f "$0")")"/";
# home folder is always thome
HOME_BASE="/home/";
# config location
@@ -66,10 +67,12 @@ CONFIG_BASE="${BASE_FOLDER}../config/";
# check config folder for .env file with HOME_LOCATION
# only use if HOME_LOCATION not yet set
if [ -z "${HOME_LOCATION}" ] && [ -f "${CONFIG_BASE}create_user.cfg" ]; then
source <(grep = ${CONFIG_BASE}create_user.cfg | sed 's/ *= */=/g')
# shellcheck source=../config/create_user.cfg"
# shellcheck disable=SC1091
source <(grep "=" "${CONFIG_BASE}create_user.cfg" | sed 's/ *= */=/g')
fi;
if [ ! -z "${HOME_LOCATION}" ]; then
if [ -n "${HOME_LOCATION}" ]; then
# must start with / as it has to be from root
if [ "${HOME_LOCATION##/*}" ]; then
echo "Home location folder must start with a slash (/): ${HOME_LOCATION}";
@@ -87,6 +90,13 @@ if [ ! -d "${HOME_FOLDER}" ]; then
echo "Home folder location not found: ${HOME_FOLDER}";
error=1;
fi;
# allow 10 to 39 length for password
if [ -n "${PASSWORD_LENGTH}" ] && ! [[ "${PASSWORD_LENGTH}" =~ ^[13][0-9]$ ]]; then
echo "Password length set error, can only be a value between 10 and 39";
error=1;
elif [ -z "${PASSWORD_LENGTH}" ]; then
PASSWORD_LENGTH=14;
fi;
# home dir error abort
if [ $error -eq 1 ]; then
exit;
@@ -104,12 +114,17 @@ ssh_keytype='';
# sshallow or sshforward
ssh_group='';
ssh_forward_ok=0;
# login shells
login_shell="/bin/bash";
no_login_shell="/sbin/nologin";
user_login_shell="";
# detect ssh authorized_keys setting
SSH_CENTRAL_AUTHORIZED_FILE_FOLDER='';
SSH_AUTHORIZED_FILE='';
# shellcheck disable=SC2013
for cf in $(grep "^AuthorizedKeysFile" /etc/ssh/sshd_config | grep "%u"); do
if [ ! -z $(echo "${cf}" | grep "%u") ]; then
SSH_CENTRAL_AUTHORIZED_FILE_FOLDER=$(echo "${cf}" | sed -e 's/%u//');
if echo "$cf" | grep -q "%u"; then
SSH_CENTRAL_AUTHORIZED_FILE_FOLDER="${cf/%%u//}";
if [ ! -d "${SSH_CENTRAL_AUTHORIZED_FILE_FOLDER}" ]; then
echo "ssh central authorized_file folder could not be found: ${SSH_CENTRAL_AUTHORIZED_FILE_FOLDER}";
exit;
@@ -126,24 +141,21 @@ if [ ! -d "${ROOT_FOLDER}${output_zip_folder}" ]; then
mkdir "${ROOT_FOLDER}${output_zip_folder}";
fi;
# check if password generate software is installed
# if [ ! command -v pwgen &> /dev/null ]; then
if [ -z $(command -v pwgen) ]; then
if [ -z "$(command -v pwgen)" ]; then
echo "Missing pwgen application, aborting";
error=1;
fi;
# check for zip
# if [ ! command -v zip &> /dev/null ]; then
if [ -z $(command -v zip) ]; then
if [ -z "$(command -v zip)" ]; then
echo "Missing zip application, aborting";
error=1;
fi;
# check if sshallow or sshfoward group exists
if [ -z $(cat /etc/group | grep "sshallow:") ]; then
if ! grep -q "sshallow:" "/etc/group"; then
echo "Missing ssh access group: sshallow";
error=1;
fi;
# flag if we can set ssh forward
if [ ! -z $(cat /etc/group | grep "sshforward:") ]; then
if ! grep -q "sshforward:" "/etc/group"; then
ssh_forward_ok=1;
fi;
# check if user list file exists
@@ -152,10 +164,10 @@ if [ ! -f "${ROOT_FOLDER}${input_file}" ]; then
error=1;
fi;
# make sure my own folder is owned by root and 600 (except for testing)
if [ $(stat -c %a .) != "600" ]; then
if [ "$(stat -c %a .)" != "600" ]; then
echo "!!!! RECOMMENDED TO HAVE BASE FOLDER SET TO '600' AND USER 'root' !!!!"
fi;
if [ $(whoami) != "root" ]; then
if [ "$(whoami)" != "root" ]; then
if [ ${TEST} -eq 0 ] && [ ${INFO} -eq 0 ]; then
echo "Script must be run as root user";
error=1;
@@ -164,6 +176,12 @@ if [ $(whoami) != "root" ]; then
fi;
fi;
# do not allow test and info at the same
if [ ${TEST} -eq 1 ] && [ ${INFO} -eq 1 ]; then
echo "Cannot have --test and --info option at the same time";
error=1;
fi;
# exit if not -g parameter set
if [ $GO -eq 0 ]; then
echo "Script has to be run with -g option for actual user creation.";
@@ -175,58 +193,99 @@ if [ $error -eq 1 ]; then
exit;
fi;
LOG="${BASE_FOLDER}/../log/user_management.log";
function write_log()
{
text="${1}";
do_echo="${2}";
log_prefix="";
# log prefix for testing
if [ ${TEST} -eq 1 ]; then
log_prefix="[TEST] ";
fi;
# write log not in info run
if [ ${INFO} -eq 0 ]; then
echo "[$(date +"%F %T")] [$0] ${log_prefix}${text}" >> "${LOG}";
fi;
if [ "${do_echo}" = "1" ]; then
echo "${text}";
fi;
}
write_log "START SCRIPT RUN";
# used for test run only
overall_run_error=0;
# MARK: LOOP START
# create users
cat "${ROOT_FOLDER}${input_file}" |
while read i; do
while read -r i; do
# run error for one row
run_error=0;
# skip rows start with # (comment)
if [[ "${i}" =~ ^\# ]]; then
continue;
fi;
# log create inly on not info
if [ ${INFO} -eq 0 ]; then
write_log "[CREATE] ROW: $i";
fi;
# MARK: VALUES CHECK
# POS 2: make lower case, remove spaces
username=$(echo "${i}" | cut -d ";" -f 2 | tr A-Z a-z | tr -d ' ');
username=$(echo "${i}" | cut -d ";" -f 2 | tr "[:upper:]" "[:lower:]" | tr -d ' ');
# check username is alphanumeric with .
if ! [[ "${username}" =~ ^[a-z0-9]+([.a-z0-9_-]+[a-z0-9])?$ ]]; then
echo "User name can only be a-z 0-9 - _ . and cannot start or end with - . or _: ${username}";
if [ ${TEST} -eq 0 ]; then
break;
fi;
run_error=1;
write_log "[ERROR] User name can only be a-z 0-9 - _ . and cannot start or end with - . or _: ${username}" "1";
fi;
# POS 3: groups
_group=$(echo "${i}" | cut -d ";" -f 3 | tr A-Z a-z | tr -d ' ');
_group=$(echo "${i}" | cut -d ";" -f 3 | tr "[:upper:]" "[:lower:]" | tr -d ' ');
group=$(echo "${_group}" | cut -d "," -f 1);
sub_group="";
# POS 4: ssh access type
ssh_access_type=$(echo "${i}" | cut -d ";" -f 4 | tr A-Z a-z | tr -d ' ');
# POS 4: ssh access type and no login flag
# no login flag
no_login_flag="";
# if there is a pipe, check, else ignore
if echo "${i}" | cut -d ";" -f 4 | grep -q "|"; then
no_login_flag=$(echo "${i}" | cut -d ";" -f 4 | cut -d "|" -f 2);
fi;
# anything set in no login shell flag, we set no login shell
if [ -n "${no_login_flag}" ]; then
user_login_shell="${no_login_shell}";
else
user_login_shell="${login_shell}";
fi;
# ssh access type
ssh_access_type=$(echo "${i}" | cut -d ";" -f 4 | cut -d "|" -f 1 | tr "[:upper:]" "[:lower:]" | tr -d ' ');
# if not allow or forward, set to access
if [ "${ssh_access_type}" != "allow" ] && [ "${ssh_access_type}" != "forward" ]; then
echo "[!!] Not valid ssh access type ${ssh_access_type}, set to allow";
ssh_access_type="allow";
run_error=1;
write_log "[ERROR] Not valid ssh access type ${ssh_access_type}" "1";
fi;
if [ $ssh_forward_ok -eq 0 ] && [ "${ssh_access_type}" = "forward" ]; then
echo "[!!!] sshforward group does not exsts, cannot set user ${username}";
if [ ${TEST} -eq 0 ]; then
break;
fi;
write_log "[ERROR] sshforward group does not exsts, cannot set user ${username}" "1";
run_error=1;
fi;
ssh_group="ssh${ssh_access_type}";
# sshallow group is always added
sub_group_opt=" -G ${ssh_group}";
sub_group_opt=("${ssh_group}");
# check if "," inside and extract sub groups
if [ -z "${_group##*,*}" ]; then
sub_group=$(echo "${_group}" | cut -d "," -f 2-);
sub_group_opt=" -G ${sub_group}";
sub_group_opt+=("${sub_group}");
fi;
# POS 5: do we have a password preset
_password=$(echo "${i}" | cut -d ";" -f 5);
# POS 6: override host name, lowercase and spaces removed
_hostname=$(echo "${i}" | cut -d ";" -f 6 | tr A-Z a-z | tr -d ' ');
_hostname=$(echo "${i}" | cut -d ";" -f 6 | tr "[:upper:]" "[:lower:]" | tr -d ' ');
if [ -z "${_hostname}" ]; then
hostname=${host};
else
hostname=${_hostname};
fi;
# POS 7: ssh keytype override
_ssh_keytype=$(echo "${i}" | cut -d ";" -f 7 | tr A-Z a-z | tr -d ' ');
_ssh_keytype=$(echo "${i}" | cut -d ";" -f 7 | tr "[:upper:]" "[:lower:]" | tr -d ' ');
if [ "${_ssh_keytype}" = "rsa" ]; then
ssh_keytype="${_ssh_keytype}";
#echo "[!!] BACKWARDS COMPATIBLE RSA TYPE SELECTION [!!]";
@@ -235,24 +294,35 @@ while read i; do
fi;
# user & group not set
if [ -z "${username}" ] || [ -z "${_group}" ]; then
echo "[!!!!!] Missing user or group entry for ${username}/${_group}";
echo "[*** ABORT RUN ***]"
if [ ${TEST} -eq 0 ]; then
break;
fi;
run_error=1;
write_log "[ERROR] Missing user or group entry for ${username}/${_group}" "1";
else
group_error=0;
# check group names valid
for create_group in ${_group//,/ }; do
if ! [[ "${create_group}" =~ ^[a-z0-9]+([a-z0-9_-]+[a-z0-9])?$ ]]; then
echo "Group name can only be a-z 0-9 - _ and cannot start or end with - or _: ${create_group}";
write_log "[ERROR] Group name can only be a-z 0-9 - _ and cannot start or end with - or _: ${create_group}" "1";
group_error=1;
fi;
done;
if [ $group_error -eq 1 ] && [ ${TEST} -eq 0 ]; then
break;
if [ $group_error -eq 1 ]; then
run_error=1;
fi;
fi;
# error & test -> break
if [ ${run_error} -eq 1 ]; then
overall_run_error=1;
write_log "[*** ABORT RUN ***]" "1";
# end if not test and not info
if [ ${TEST} -eq 0 ] && [ ${INFO} -eq 0 ]; then
break;
else
continue;
fi;
fi;
# MARK: SSH NAMES SET
# SSH file name part without folder
ssh_keygen_id="${hostname}${separator}${group}${separator}${username}${separator}${ssh_keytype}.pem";
# the full file including folder name
@@ -262,24 +332,25 @@ while read i; do
# check existing pub file
ssh_keyfile_check_pub="${ROOT_FOLDER}${SSH_KEYGEN_FOLDER_CREATED_PUB}${ssh_keygen_id}.pub";
# MARK: INFO
if [ ${INFO} -eq 1 ]; then
info_string="User: '${username}:${group}(${sub_group});${ssh_group}', SSH: ${ssh_keygen_id}";
# test if pub file exists or not, test if user exists
echo -n "User: '${username}:${group}(${sub_group});${ssh_group}', SSH: ${ssh_keygen_id}";
if getent passwd ${username} > /dev/null 2>&1; then
echo -n ", User exists";
if getent passwd "${username}" > /dev/null 2>&1; then
info_string="${info_string}, User exists";
fi;
if [ -f "${ssh_keyfile_check_pub}" ]; then
echo -n ", SSH Pub key OK";
info_string="${info_string}, SSH Pub key OK";
fi;
# line break
echo "";
echo "${info_string}";
continue;
fi;
# MARK: CREATE
# add group for each entry in _group
for create_group in ${_group//,/ }; do
if [ ${TEST} -eq 0 ]; then
groupadd -f ${create_group};
groupadd -f "${create_group}";
else
echo "$> groupadd -f ${create_group}";
fi;
@@ -287,14 +358,20 @@ while read i; do
# check if user is not already created
# if getent passwd ${username} > /dev/null 2>&1; then
if id "${username}" &>/dev/null; then
echo "-- Skip '${username}:${group}(${sub_group})'";
write_log "-- Skip '${username}:${group}(${sub_group})'" "1";
else
echo "++ Create '${username}:${group}(${sub_group})'";
write_log "++ Create '${username}:${group}(${sub_group})'" "1";
params=(
"-c" "$(date +"%F")" "-s" "${user_login_shell}"
"-g" "${group}" "-G" "$(IFS=, ; echo "${sub_group_opt[*]}")"
"-d" "${HOME_FOLDER}${username}" "-m"
"${username}"
);
if [ ${TEST} -eq 0 ]; then
# comment is user create time
useradd -c `date +"%F"` -s /bin/bash -g ${group}${sub_group_opt} -d "${HOME_FOLDER}${username}" -m ${username};
useradd "${params[@]}";
else
echo "$> useradd -c `date +"%F"` -s /bin/bash -g ${group}${sub_group_opt} -d "${HOME_FOLDER}${username}" -m ${username}";
echo "$> useradd ${params[*]}";
fi;
fi;
# set the auth file
@@ -309,16 +386,20 @@ while read i; do
# Note we only create a password if we need it
# password + store pwgen 10 1 -1
if [ -z "${_password}" ]; then
password=$(printf "%s" $(pwgen 14 1));
password=$(printf "%s" "$(pwgen "${PASSWORD_LENGTH}" 1)");
elif [ "${_password}" = "SET_NO_PASSWORD" ]; then
# set empty
echo "* No password set";
password="";
else
echo "! Override password set";
password=${_password};
fi;
# create SSH key
echo " > Create ssh key-pair '${ssh_keyfile}'";
write_log " > Create ssh key-pair: ${ssh_keyfile}" "1";
if [ ${TEST} -eq 0 ]; then
ssh-keygen \
-t ${ssh_keytype} \
-t "${ssh_keytype}" \
-f "${ssh_keyfile}" \
-C "${hostname}: ${username}@${group}" \
-a 100 -N "${password}"
@@ -328,50 +409,51 @@ while read i; do
else
found='';
if [ -f "${SSH_AUTHORIZED_FILE}" ]; then
found=$(grep "$(cat ${ssh_keyfile_check_pub})" ${SSH_AUTHORIZED_FILE});
found=$(grep "$(cat "${ssh_keyfile_check_pub}")" "${SSH_AUTHORIZED_FILE}");
fi;
if [ ! -z "${found}" ]; then
if [ -n "${found}" ]; then
skip_ssh=1;
echo "-- Skip SSH Key creation: ${ssh_keygen_id}.pub";
write_log "-- Skip SSH Key creation: ${ssh_keygen_id}.pub" "1";
else
# override previously set with stored one
ssh_keyfile_pub=${ssh_keyfile_check_pub};
echo " < Use existing public ssh key '${ssh_keygen_id}.pub'";
write_log " < Use existing public ssh key: ${ssh_keygen_id}.pub" "1";
# Password already set notification
fi;
password="[ALREADY SET]";
fi;
if [ ${skip_ssh} -eq 0 ]; then
# MARK: SSH CREATE
# write login info to output file
if [ ${TEST} -eq 0 ]; then
create_output_file="${ROOT_FOLDER}${output_file}";
else
create_output_file="${ROOT_FOLDER}${output_file}.TEST";
fi;
echo $(date +"%F %T")";"${host}";"${_hostname}";"${username}";"${password}";"${ssh_access_type} >> ${create_output_file};
echo "$(date +"%F %T");${host};${_hostname};${username};${password};${ssh_access_type}" >> "${create_output_file}";
# create folder only if we do not have central
# create the SSH foler and authorized access file with correct permissions
if [ -z "${SSH_CENTRAL_AUTHORIZED_FILE_FOLDER}" ]; then
echo " > Create .ssh folder";
if [ ${TEST} -eq 0 ]; then
mkdir ${HOME_FOLDER}${username}/.ssh/;
mkdir "${HOME_FOLDER}${username}/.ssh/";
else
echo "$> mkdir ${HOME_FOLDER}${username}/.ssh/";
echo "$> mkdir \"${HOME_FOLDER}${username}/.ssh/\"";
fi;
fi;
# add
echo " > Add public into authorized_keys file";
if [ ${TEST} -eq 0 ]; then
if
[ ! -z "${SSH_CENTRAL_AUTHORIZED_FILE_FOLDER}" ] &&
[ -n "${SSH_CENTRAL_AUTHORIZED_FILE_FOLDER}" ] &&
[ -f "${SSH_AUTHORIZED_FILE}" ];
then
chattr -i ${SSH_AUTHORIZED_FILE};
chattr -i "${SSH_AUTHORIZED_FILE}";
fi;
cat "${ssh_keyfile_pub}" > ${SSH_AUTHORIZED_FILE};
cat "${ssh_keyfile_pub}" > "${SSH_AUTHORIZED_FILE}";
else
if
[ ! -z "${SSH_CENTRAL_AUTHORIZED_FILE_FOLDER}" ] &&
[ -n "${SSH_CENTRAL_AUTHORIZED_FILE_FOLDER}" ] &&
[ -f "${SSH_AUTHORIZED_FILE}" ];
then
echo "$> chattr -i ${SSH_AUTHORIZED_FILE}";
@@ -382,42 +464,54 @@ while read i; do
if [ -z "${SSH_CENTRAL_AUTHORIZED_FILE_FOLDER}" ]; then
echo " > Secure home directory folder .ssh and authorized_keys file";
if [ ${TEST} -eq 0 ]; then
chown -R ${username}:${group} ${HOME_FOLDER}${username}/.ssh/;
chmod 700 ${HOME_FOLDER}${username}/.ssh/;
chmod 600 ${SSH_AUTHORIZED_FILE};
chown -R "${username}":"${group}" "${HOME_FOLDER}${username}/.ssh/";
chmod 700 "${HOME_FOLDER}${username}/.ssh/";
chmod 600 "${SSH_AUTHORIZED_FILE}";
else
echo "$> chown -R ${username}:${group} ${HOME_FOLDER}${username}/.ssh/";
echo "$> chmod 700 ${HOME_FOLDER}${username}/.ssh/";
echo "$> chmod 600 ${SSH_AUTHORIZED_FILE}";
echo "$> chown -R \"${username}\":\"${group}\" \"${HOME_FOLDER}${username}/.ssh/\"";
echo "$> chmod 700 \"${HOME_FOLDER}${username}/.ssh/\"";
echo "$> chmod 600 \"${SSH_AUTHORIZED_FILE}\"";
fi;
else
echo " > Secure central authorized_keys file";
if [ ${TEST} -eq 0 ]; then
chown ${username}:root ${SSH_AUTHORIZED_FILE};
chmod 400 ${SSH_AUTHORIZED_FILE};
chown "${username}":root "${SSH_AUTHORIZED_FILE}";
chmod 400 "${SSH_AUTHORIZED_FILE}";
# set +i so user can't change file
chattr +i ${SSH_AUTHORIZED_FILE};
chattr +i "${SSH_AUTHORIZED_FILE}";
else
echo "$> chown ${username}:root ${SSH_AUTHORIZED_FILE}";
echo "$> chmod 400 ${SSH_AUTHORIZED_FILE}";
echo "$> chattr +i ${SSH_AUTHORIZED_FILE}";
echo "$> chown \"${username}\":root \"${SSH_AUTHORIZED_FILE}\"";
echo "$> chmod 400 \"${SSH_AUTHORIZED_FILE}\"";
echo "$> chattr +i \"${SSH_AUTHORIZED_FILE}\"";
fi;
fi;
fi;
done;
done <<< "$(cat "${ROOT_FOLDER}${input_file}")";
# End before anything because this is just info run
if [ ${INFO} -eq 1 ]; then
exit;
fi;
# MARK: ZIP FILE CREATE
# check if there are any files in the SSH_KEYGEN_FOLDER, else skip zip file creation and file move
has_pem_files=0;
if (shopt -s nullglob dotglob; f=("${SSH_KEYGEN_FOLDER}"*".pem"*); ((${#f[@]}))); then
has_pem_files=1;
fi;
# zip everything and remove data in ssh key folder, delete output file with passwords
if [ ${TEST} -eq 0 ]; then
zip -r \
"${ROOT_FOLDER}${output_zip_folder}${output_zip}" \
"${input_file}" \
"${output_file}" \
"${SSH_KEYGEN_FOLDER}" \
-x\*.gitignore;
if [ "${has_pem_files}" -eq 1 ]; then
zip -r \
"${ROOT_FOLDER}${output_zip_folder}${output_zip}" \
"${input_file}" \
"${output_file}" \
"${SSH_KEYGEN_FOLDER}" \
-x\*.gitignore;
echo "Download: ${ROOT_FOLDER}${output_zip_folder}${output_zip}";
else
echo "Skip ZIP file creation, no pem files";
fi;
else
echo "zip -r \\"
echo "${ROOT_FOLDER}${output_zip_folder}${output_zip} \\"
@@ -425,19 +519,28 @@ else
echo "${output_file} \\"
echo "${SSH_KEYGEN_FOLDER} \\"
echo "-x\*.gitignore;"
echo "Download: ${ROOT_FOLDER}${output_zip_folder}${output_zip}";
fi;
echo "Download: ${ROOT_FOLDER}${output_zip_folder}${output_zip}";
# cleam up user log file and ssh keys
if [ ${TEST} -eq 0 ]; then
# move pub to created folders
mv "${ROOT_FOLDER}${SSH_KEYGEN_FOLDER}"*.pub "${ROOT_FOLDER}${SSH_KEYGEN_FOLDER_CREATED_PUB}";
# delete the rest
rm "${ROOT_FOLDER}${output_file}";
rm "${ROOT_FOLDER}${SSH_KEYGEN_FOLDER}"*;
if [ "${has_pem_files}" -eq 1 ]; then
# move pub to created folders
mv "${ROOT_FOLDER}${SSH_KEYGEN_FOLDER}"*.pub "${ROOT_FOLDER}${SSH_KEYGEN_FOLDER_CREATED_PUB}";
# delete the rest
rm "${ROOT_FOLDER}${output_file}";
rm "${ROOT_FOLDER}${SSH_KEYGEN_FOLDER}"*;
else
echo "Skip pub file move and cleanup, no pem files";
fi;
else
echo "$> mv ${ROOT_FOLDER}${SSH_KEYGEN_FOLDER}*.pub ${ROOT_FOLDER}${SSH_KEYGEN_FOLDER_CREATED_PUB};";
echo "$> rm ${ROOT_FOLDER}${output_file}";
echo "$> rm ${ROOT_FOLDER}${SSH_KEYGEN_FOLDER}*";
fi;
# MARK: TEST ERROR INFO
if [ ${TEST} -eq 1 ] && [ ${overall_run_error} -eq 1 ]; then
echo "[ERROR] Some errors occoured during the run, they will prohibit the live run of this script";
fi;
# __END__

View File

@@ -15,10 +15,10 @@ TEST=0; # do not run any actions
BACKUP=1;
while getopts ":tb" opt; do
case "${opt}" in
t|test)
t) # var/log/secure*bz2
TEST=1;
;;
b|nobackup)
b) # nobackup
BACKUP=0;
;;
\?)
@@ -32,7 +32,7 @@ while getopts ":tb" opt; do
done;
shift "$((OPTIND-1))"
if [ $(whoami) != "root" ]; then
if [ "$(whoami)" != "root" ]; then
if [ ${TEST} -eq 0 ]; then
echo "Script must be run as root user";
exit;
@@ -53,27 +53,23 @@ timestamp=$(date +%Y%m%d-%H%M%S);
# character to set getween info blocks
separator="#";
# base folder for all data
BASE_FOLDER=$(dirname $(readlink -f $0))"/";
BASE_FOLDER=$(dirname "$(readlink -f "$0")")"/";
root_folder="${BASE_FOLDER}../";
backup_folder="${BASE_FOLDER}../backup/";
SSH_KEYGEN_FOLDER_CREATED_PUB='ssh-keygen-created-pub/';
# SSH_KEYGEN_FOLDER_CREATED_PUB='ssh-keygen-created-pub/';
input_file='user_list.txt';
user_list_file="${root_folder}${input_file}";
# log file
LOG="${BASE_FOLDER}/../log/delete_user."$(date +"%F_%H%m%S");
if [ ${TEST} -eq 0 ]; then
LOG="${LOG}.log";
else
LOG="${LOG}.test.log";
fi;
HISTORY="${BASE_FOLDER}/../log/delete_user.log";
# ignore users (root and admin users)
ignore_users=('root' 'ec2-user' 'ubuntu' 'admin');
# detect ssh authorized_keys setting
SSH_CENTRAL_AUTHORIZED_FILE_FOLDER='';
SSH_AUTHORIZED_FILE='';
for cf in $(grep "^AuthorizedKeysFile" /etc/ssh/sshd_config | grep "%u"); do
if [ ! -z $(echo "${cf}" | grep "%u") ]; then
SSH_CENTRAL_AUTHORIZED_FILE_FOLDER=$(echo "${cf}" | sed -e 's/%u//');
# shellcheck disable=SC2013
for cf in $(grep "^AuthorizedKeysFile" "/etc/ssh/sshd_config" | grep "%u"); do
if echo "$cf" | grep -q "%u"; then
SSH_CENTRAL_AUTHORIZED_FILE_FOLDER="${cf/%%u//}";
if [ ! -d "${SSH_CENTRAL_AUTHORIZED_FILE_FOLDER}" ]; then
echo "ssh central authorized_file folder could not be found: ${SSH_CENTRAL_AUTHORIZED_FILE_FOLDER}";
exit;
@@ -86,17 +82,39 @@ if [ ! -f "${user_list_file}" ]; then
exit;
fi;
LOG="${BASE_FOLDER}/../log/user_management.log";
function write_log()
{
text="${1}";
do_echo="${2}";
log_prefix="";
# log prefix for testing
if [ ${TEST} -eq 1 ]; then
log_prefix="[TEST] ";
fi;
# write log not in info run
echo "[$(date +"%F %T")] [$0] ${log_prefix}${text}" >> "${LOG}";
if [ "${do_echo}" = "1" ]; then
echo "${text}";
fi;
}
write_log "START SCRIPT RUN";
# used for test run only
overall_run_error=0;
# $1 ... $n
for username in "$@"; do
error=0;
# skip if there is an option hidden
# shellcheck disable=SC2154
if [[ ${_arg:0:1} = "-" ]]; then
continue;
fi;
# skip ignore users, note that if a user is not in the sshallow list anyway
# we skip them too, this is just in case check
if [[ " ${ignore_users[*]} " =~ " ${username} " ]]; then
echo "[!] User ${username} is in the ignore user list";
if [[ " ${ignore_users[*]} " =~ [[:space:]]${username}[[:space:]] ]]; then
write_log "[!] User ${username} is in the ignore user list" "1";
continue;
fi;
@@ -104,31 +122,33 @@ for username in "$@"; do
# if missing in or another do not continue
if ! id "${username}" &>/dev/null; then
# not in passwd
echo "[!!!] User ${username} does not exist in /etc/passwd";
write_log "[ERRPR] User ${username} does not exist in /etc/passwd" "1";
error=1;
fi;
user_list_entry=$(grep "${username}" "${user_list_file}");
if [ -z "${user_list_entry}" ]; then
echo "[!!!] User ${username} does not exist in user_list.txt file";
write_log "[ERROR] User ${username} does not exist in user_list.txt file" "1";
error=1;
elif [[ "${user_list_entry}" =~ ^#DELETED ]]; then
echo "[!!!] User ${username} is flagged as deleted in user_list.txt file";
write_log "[ERROR] User ${username} is flagged as deleted in user_list.txt file" "1";
error=1;
fi;
if [ $error -eq 1 ]; then
overall_run_error=1;
write_log "[*** ABORT RUN ***]" "1";
if [ ${TEST} -eq 0 ]; then
break;
fi;
fi;
echo "=> Delete: ${username}";
write_log "=> Delete: ${username}" "1";
# ssh authorized file
SSH_AUTHORIZED_FILE="${SSH_CENTRAL_AUTHORIZED_FILE_FOLDER}${username}";
# make backup from /home
if [ ${BACKUP} -eq 1 ]; then
home_folder=$(getent passwd ${username} | cut -d ":" -f 6);
home_folder=$(getent passwd "${username}" | cut -d ":" -f 6);
backup_file="${backup_folder}${host}${separator}${username}.${timestamp}.tar.bz2";
files_list="${home_folder}";
if [ -f "${SSH_AUTHORIZED_FILE}" ]; then
@@ -136,7 +156,7 @@ for username in "$@"; do
fi;
echo "[0] Backup ${files_list} to ${backup_file}";
if [ ${TEST} -eq 0 ]; then
tar cfjp "${backup_file}" ${file_list};
tar cfjp "${backup_file}" "${files_list}";
else
echo "$> tar cfjp \"${backup_file}\" ${files_list};";
fi;
@@ -144,7 +164,7 @@ for username in "$@"; do
echo "[1] Remove user + home dir";
if [ ${TEST} -eq 0 ]; then
userdel -r ${username}
userdel -r "${username}"
else
echo "$> userdel -r ${username}";
fi;
@@ -162,7 +182,7 @@ for username in "$@"; do
fi;
else
# Not critical error
echo "[?] Cannot find ${SSH_AUTHORIZED_FILE}";
write_log "[?] Cannot find ${SSH_AUTHORIZED_FILE}" "1";
fi;
# Update user_list.txt file and add # for the line
@@ -172,11 +192,17 @@ for username in "$@"; do
if [ ${TEST} -eq 0 ]; then
sed -i -e "s/^\([A-Za-z0-9]\{1,\};${username};\)/#DELETED-${delete_date}:\1/" "${user_list_file}";
else
# shellcheck disable=SC2028
echo "$> sed -i -e \"s/^\([A-Za-z0-9]\{1,\};${username};\)/#DELETED-${delete_date}:\1/\" \"${user_list_file}\";";
fi;
echo $(date +"%F %T")";${host};${username}" >> "${LOG}";
echo "$(date +"%F %T");${host};${username};${TEST}" >> "${HISTORY}";
done;
# MARK: TEST ERROR INFO
if [ ${TEST} -eq 1 ] && [ ${overall_run_error} -eq 1 ]; then
echo "[ERROR] Some errors occoured during the run, they will prohibit the live run of this script";
fi;
# __END__

View File

@@ -9,14 +9,18 @@
TEST=0; # no delete, just print
while getopts ":t" opt; do
case "${opt}" in
t|test)
t) # test
TEST=1;
;;
\?)
echo "";
echo "-t test run, do not lock users";
;;
esac;
done;
shift "$((OPTIND-1))"
if [ $(whoami) != "root" ]; then
if [ "$(whoami)" != "root" ]; then
if [ ${TEST} -eq 0 ]; then
echo "Script must be run as root user";
exit;
@@ -34,7 +38,7 @@ fi;
ignore_users=('root' 'ec2-user' 'ubuntu' 'admin');
# ssh reject group
ssh_reject_group="sshreject";
if [ -z $(cat /etc/group | grep "${ssh_reject_group}:") ]; then
if ! grep -q "${ssh_reject_group}:" /etc/group; then
echo "Missing ssh reject group: ${ssh_reject_group}";
exit;
fi;
@@ -42,27 +46,48 @@ ssh_allow_group="sshallow";
ssh_forward_group="sshforward";
user_group_tpl="gpasswd -d %s %s\ngpasswd -a %s %s\n";
LOG="${BASE_FOLDER}/../log/user_management.log";
function write_log()
{
text="${1}";
do_echo="${2}";
log_prefix="";
# log prefix
if [ ${TEST} -eq 1 ]; then
log_prefix="TEST";
fi;
if [ -n "${log_prefix}" ]; then
log_prefix="[${log_prefix}] ";
fi;
echo "[$(date +"%F %T")] [$0] ${log_prefix}${text}" >> "${LOG}";
if [ "${do_echo}" = "1" ]; then
echo "${text}";
fi;
}
write_log "START SCRIPT RUN";
echo "--------------------->"
# $1 ... $n
for username in "$@"; do
# skip if there is an option hidden
# shellcheck disable=SC2154
if [[ ${_arg:0:1} = "-" ]]; then
continue;
fi;
# skip ignore users, note that if a user is not in the sshallow list anyway
# we skip them too, this is just in case check
if [[ " ${ignore_users[*]} " =~ " ${username} " ]]; then
echo "[!] User ${username} is in the ignore user list";
if [[ " ${ignore_users[*]} " =~ [[:space:]]${username}[[:space:]] ]]; then
write_log "[ERROR] User ${username} is in the ignore user list" "1";
continue;
fi;
# check that user exists in passwd
if ! id "${username}" &>/dev/null; then
echo "[!] User ${username} does not exists in /etc/passwd file";
write_log "[ERROR] User ${username} does not exists in /etc/passwd file" "1";
continue;
fi;
# if not check if in reject list
if id -nGz "${username}" | grep -qzxF "${ssh_reject_group}"; then
echo "[.] User ${username} already in the ${ssh_reject_group} list";
write_log "[.] User ${username} already in the ${ssh_reject_group} list";
continue;
fi;
# check if user is in sshallow/forward list
@@ -72,16 +97,17 @@ for username in "$@"; do
fi;
# if user is in ssh allow group and ALSO in ssh forward group -> bad
if id -nGz "${username}" | grep -qzxF "${ssh_forward_group}"; then
if [ ! -z "${ssh_remove_group}" ]; then
echo "[!!!! ERROR !!!!] User ${username} exists in both ${ssh_allow_group} and ${ssh_forward_group} group which should not be allowed. Remove user from one group and run script again.";
if [ -n "${ssh_remove_group}" ]; then
write_log "[!!!! ERROR !!!!] User ${username} exists in both ${ssh_allow_group} and ${ssh_forward_group} group which should not be allowed. Remove user from one group and run script again." "1";
break;
fi;
ssh_remove_group="${ssh_forward_group}";
fi;
if [ ! -z "${ssh_remove_group}" ]; then
if [ -n "${ssh_remove_group}" ]; then
# remove user from ssh group and add to reject groups
echo "[*] User ${username} will be removed from ${ssh_remove_group}";
write_log "[*] User ${username} will be removed from ${ssh_remove_group}" "1";
if [ ${TEST} -eq 1 ]; then
# shellcheck disable=SC2059
printf "${user_group_tpl}" "${username}" "${ssh_remove_group}" "${username}" "${ssh_reject_group}";
else
gpasswd -d "${username}" "${ssh_remove_group}";
@@ -89,7 +115,7 @@ for username in "$@"; do
fi;
else
# skip not ssh user
echo "[?] User ${username} not in any ssh allow/foward groups";
write_log "[?] User ${username} not in any ssh allow/foward groups" "1";
fi;
done;

View File

@@ -12,15 +12,15 @@ OLD_USERNAME="";
NEW_USERNAME="";
while getopts ":to:n:" opt; do
case "${opt}" in
t|test)
t) # test
TEST=1;
;;
o|old-user)
o) # old-user
if [ -z "${OLD_USERNAME}" ]; then
OLD_USERNAME="${OPTARG}";
fi;
;;
n|new-user)
n) # new-user
if [ -z "${NEW_USERNAME}" ]; then
NEW_USERNAME="${OPTARG}";
fi;
@@ -36,7 +36,7 @@ while getopts ":to:n:" opt; do
done;
shift "$((OPTIND-1))"
if [ $(whoami) != "root" ]; then
if [ "$(whoami)" != "root" ]; then
if [ ${TEST} -eq 0 ]; then
echo "Script must be run as root user";
exit;
@@ -47,35 +47,29 @@ fi;
error=0;
host=$(hostname);
timestamp=$(date +%Y%m%d-%H%M%S);
# timestamp=$(date +%Y%m%d-%H%M%S);
# character to set getween info blocks
separator="#";
# base folder for all data
BASE_FOLDER=$(dirname $(readlink -f $0))"/";
root_folder="${BASE_FOLDER}../";
BASE_FOLDER=$(dirname "$(readlink -f "$0")")"/";
ROOT_FOLDER="${BASE_FOLDER}../";
SSH_KEYGEN_FOLDER_CREATED_PUB='ssh-keygen-created-pub/';
input_file='user_list.txt';
user_list_file="${root_folder}${input_file}";
user_list_file="${ROOT_FOLDER}${input_file}";
default_ssh_keytype='ed25519';
ssh_keytype='';
# log file
LOG="${BASE_FOLDER}/../log/rename_user."$(date +"%F_%H%m%S");
if [ ${TEST} -eq 0 ]; then
LOG="${LOG}.log";
else
LOG="${LOG}.test.log";
fi;
# ignore users (root and admin users)
ignore_users=('root' 'ec2-user' 'ubuntu' 'admin');
# detect ssh authorized_keys setting
SSH_CENTRAL_AUTHORIZED_FILE_FOLDER='';
SSH_AUTHORIZED_FILE='';
# SSH_AUTHORIZED_FILE='';
# shellcheck disable=SC2013
for cf in $(grep "^AuthorizedKeysFile" /etc/ssh/sshd_config | grep "%u"); do
if [ ! -z $(echo "${cf}" | grep "%u") ]; then
SSH_CENTRAL_AUTHORIZED_FILE_FOLDER=$(echo "${cf}" | sed -e 's/%u//');
if echo "$cf" | grep -q "%u"; then
SSH_CENTRAL_AUTHORIZED_FILE_FOLDER="${cf/%%u//}";
if [ ! -d "${SSH_CENTRAL_AUTHORIZED_FILE_FOLDER}" ]; then
echo "ssh central authorized_file folder could not be found: ${SSH_CENTRAL_AUTHORIZED_FILE_FOLDER}";
error=1;
exit;
fi;
fi;
done;
@@ -101,11 +95,11 @@ fi;
# skip ignore users, note that if a user is not in the sshallow list anyway
# we skip them too, this is just in case check
if [[ " ${ignore_users[*]} " =~ " ${OLD_USERNAME} " ]]; then
if [[ " ${ignore_users[*]} " =~ [[:space:]]${OLD_USERNAME}[[:space:]] ]]; then
echo "[!] User ${OLD_USERNAME} is in the ignore user list";
error=1;
fi;
if [[ " ${ignore_users[*]} " =~ " ${NEW_USERNAME} " ]]; then
if [[ " ${ignore_users[*]} " =~ [[:space:]]${NEW_USERNAME}[[:space:]] ]]; then
echo "[!] User ${NEW_USERNAME} is in the ignore user list";
error=1;
fi;
@@ -128,12 +122,12 @@ if [ -f "${user_list_file}" ]; then
error=1;
fi;
# if the old user exists but as DELETED -> no go
if [ ! -z $(echo "${user_list_entry}" | grep "#DELETED-") ]; then
if ! echo "${user_list_entry}" | grep -q "#DELETED-"; then
echo "[!!!] User ${OLD_USERNAME} has been flagged as deleted";
error=1;
fi;
# if new user name already exists in user list file for whatever reason
if [ $(grep "${NEW_USERNAME}" "${user_list_file}") ]; then
if grep -q "${NEW_USERNAME}" "${user_list_file}"; then
echo "[!!!] User ${NEW_USERNAME} exists in user_list.txt file";
error=1;
fi;
@@ -143,34 +137,55 @@ if [ $error -eq 1 ]; then
exit;
fi;
# log file
LOG="${BASE_FOLDER}/../log/user_management.log";
function write_log()
{
text="${1}";
do_echo="${2}";
log_prefix="";
# log prefix
if [ ${TEST} -eq 1 ]; then
log_prefix="TEST";
fi;
if [ -n "${log_prefix}" ]; then
log_prefix="[${log_prefix}] ";
fi;
echo "[$(date +"%F %T")] [$0] ${log_prefix}${text}" >> "${LOG}";
if [ "${do_echo}" = "1" ]; then
echo "${text}";
fi;
}
write_log "START SCRIPT RUN";
# parse user list entry for group/hostname/ssh type key to build ssh key list
# POS 3: groups
_group=$(echo "${user_list_entry}" | cut -d ";" -f 3 | tr A-Z a-z | tr -d ' ');
_group=$(echo "${user_list_entry}" | cut -d ";" -f 3 | tr '[:upper:]' '[:lower:]' | tr -d ' ');
group=$(echo "${_group}" | cut -d "," -f 1);
# POS 6: override host name, lowercase and spaces removed
_hostname=$(echo "${user_list_entry}" | cut -d ";" -f 6 | tr A-Z a-z | tr -d ' ');
_hostname=$(echo "${user_list_entry}" | cut -d ";" -f 6 | tr '[:upper:]' '[:lower:]' | tr -d ' ');
if [ -z "${_hostname}" ]; then
hostname=${host};
else
hostname=${_hostname};
fi;
# POS 7: ssh keytype override
_ssh_keytype=$(echo "${user_list_entry}" | cut -d ";" -f 7 | tr A-Z a-z | tr -d ' ');
_ssh_keytype=$(echo "${user_list_entry}" | cut -d ";" -f 7 | tr '[:upper:]' '[:lower:]' | tr -d ' ');
if [ "${_ssh_keytype}" = "rsa" ]; then
ssh_keytype="${_ssh_keytype}";
else
ssh_keytype=${default_ssh_keytype};
fi;
echo "* Rename ${OLD_USERNAME} to ${NEW_USERNAME}";
write_log "* Rename ${OLD_USERNAME} to ${NEW_USERNAME}" "1";
old_home_dir=$(getent passwd "${OLD_USERNAME}" | cut -d: -f6);
new_home_dir=$(echo "${old_home_dir}" | sed -e "s/\/${OLD_USERNAME}$/\/${NEW_USERNAME}/");
# rename user
if [ $TEST -eq 0 ]; then
echo "usermod with ${new_home_dir}";
usermod -l ${NEW_USERNAME} -m -d "${new_home_dir}" ${OLD_USERNAME};
usermod -l "${NEW_USERNAME}" -m -d "${new_home_dir}" "${OLD_USERNAME}";
else
echo "$> usermod -l ${NEW_USERNAME} -m -d \"${new_home_dir}\" ${OLD_USERNAME};";
fi
@@ -201,7 +216,7 @@ NEW_SSH_AUTHORIZED_FILE="${SSH_CENTRAL_AUTHORIZED_FILE_FOLDER}${NEW_USERNAME}";
if [ -f "${OLD_SSH_AUTHORIZED_FILE}" ]; then
if [ $TEST -eq 0 ]; then
echo "rename to ${NEW_SSH_AUTHORIZED_FILE}";
write_log "rename to ${NEW_SSH_AUTHORIZED_FILE}" "1";
chattr -i "${OLD_SSH_AUTHORIZED_FILE}";
mv "${OLD_SSH_AUTHORIZED_FILE}" "${NEW_SSH_AUTHORIZED_FILE}";
chattr +i "${NEW_SSH_AUTHORIZED_FILE}";
@@ -211,7 +226,7 @@ if [ -f "${OLD_SSH_AUTHORIZED_FILE}" ]; then
echo "$> chattr +i \"${NEW_SSH_AUTHORIZED_FILE}\";";
fi;
else
echo "[?] ${OLD_SSH_AUTHORIZED_FILE} is missing";
write_log "[?] ${OLD_SSH_AUTHORIZED_FILE} is missing" "1";
fi;
# rename keygen public file
@@ -220,13 +235,13 @@ NEW_ssh_keygen_pub="${ROOT_FOLDER}${SSH_KEYGEN_FOLDER_CREATED_PUB}${hostname}${s
if [ -f "${OLD_ssh_keygen_pub}" ]; then
if [ $TEST -eq 0 ]; then
echo "rename to ${NEW_ssh_keygen_pub}";
write_log "rename to ${NEW_ssh_keygen_pub}" "1";
mv "${OLD_ssh_keygen_pub}" "${NEW_ssh_keygen_pub}";
else
echo "$> mv \"${OLD_ssh_keygen_pub}\" \"${NEW_ssh_keygen_pub}\";";
fi;
else
echo "[?] ${OLD_ssh_keygen_pub} is missing";
write_log "[?] ${OLD_ssh_keygen_pub} is missing" "1";
fi;
# rename entry in user list txt file
@@ -234,6 +249,8 @@ if [ $TEST -eq 0 ]; then
echo "update ${user_list_file}";
sed -i -e "s/^\([A-Za-z0-9]\{1,\}\);${OLD_USERNAME};/\1;${NEW_USERNAME};/" "${user_list_file}";
else
# just as is print the sed command from above
# shellcheck disable=SC2028
echo "$> sed -i -e \"s/^\([A-Za-z0-9]\{1,\}\);${OLD_USERNAME};/\1;${NEW_USERNAME};/\" \"${user_list_file}\";";
fi;

View File

@@ -10,19 +10,24 @@ TEST=0; # no delete, just print
SSH_GROUP_ADD='';
while getopts ":ts:" opt; do
case "${opt}" in
t|test)
t) # test
TEST=1;
;;
s|sshgroup)
s) # sshgroup
if [ -z "${SSH_GROUP_ADD}" ]; then
SSH_GROUP_ADD=${OPTARG};
fi;
;;
\?)
echo "";
echo "-t Test only, do not change user lock status";
echo "-s <group> Override ssh group from user_list.txt for this user";
;;
esac;
done;
shift "$((OPTIND-1))"
if [ $(whoami) != "root" ]; then
if [ "$(whoami)" != "root" ]; then
if [ ${TEST} -eq 0 ]; then
echo "Script must be run as root user";
exit;
@@ -36,19 +41,19 @@ if [ $# -eq 0 ]; then
exit;
fi;
if [ ! -z "${SSH_GROUP_ADD}" ] && [ "${SSH_GROUP_ADD}" != "allow" ] && [ "${SSH_GROUP_ADD}" != "forward" ]; then
if [ -n "${SSH_GROUP_ADD}" ] && [ "${SSH_GROUP_ADD}" != "allow" ] && [ "${SSH_GROUP_ADD}" != "forward" ]; then
echo "sshgroup option can only be 'allow' or 'forward'";
exit;
fi;
BASE_FOLDER=$(dirname $(readlink -f $0))"/";
BASE_FOLDER=$(dirname "$(readlink -f "$0")")"/";
root_folder="${BASE_FOLDER}../";
input_file='user_list.txt';
# ignore users (root and admin users)
ignore_users=('root' 'ec2-user' 'ubuntu' 'admin');
# ssh reject group
ssh_reject_group="sshreject";
if [ -z $(cat /etc/group | grep "${ssh_reject_group}:") ]; then
if ! grep -q "${ssh_reject_group}:" /etc/group; then
echo "Missing ssh reject group: ${ssh_reject_group}";
exit;
fi;
@@ -56,31 +61,52 @@ ssh_allow_group="sshallow";
ssh_forward_group="sshforward";
user_group_tpl="gpasswd -d %s %s\ngpasswd -a %s %s\n";
LOG="${BASE_FOLDER}/../log/user_management.log";
function write_log()
{
text="${1}";
do_echo="${2}";
log_prefix="";
# log prefix
if [ ${TEST} -eq 1 ]; then
log_prefix="TEST";
fi;
if [ -n "${log_prefix}" ]; then
log_prefix="[${log_prefix}] ";
fi;
echo "[$(date +"%F %T")] [$0] ${log_prefix}${text}" >> "${LOG}";
if [ "${do_echo}" = "1" ]; then
echo "${text}";
fi;
}
write_log "START SCRIPT RUN";
echo "--------------------->"
# $1 ... $n
for username in "$@"; do
# skip if there is an option hidden
# shellcheck disable=SC2154
if [[ ${_arg:0:1} = "-" ]]; then
continue;
fi;
# skip ignore users, note that if a user is not in the sshallow list anyway
# we skip them too, this is just in case check
if [[ " ${ignore_users[*]} " =~ " ${username} " ]]; then
echo "[!] User ${username} is in the ignore user list";
if [[ " ${ignore_users[*]} " =~ [[:space:]]${username}[[:space:]] ]]; then
write_log "[ERROR] User ${username} is in the ignore user list" "1";
continue;
fi;
# check that user exists in passwd
if ! id "${username}" &>/dev/null; then
echo "[!] User ${username} does not exists in /etc/passwd file";
write_log "[ERROR] User ${username} does not exists in /etc/passwd file" "1";
continue;
fi;
# check if already in OK groups
if id -nGz "${username}" | grep -qzxF "${ssh_allow_group}"; then
echo "[.] User ${username} already in the ${ssh_allow_group} list";
write_log "[.] User ${username} already in the ${ssh_allow_group} list" "1";
continue;
fi;
if id -nGz "${username}" | grep -qzxF "${ssh_forward_group}"; then
echo "[.] User ${username} already in the ${ssh_forward_group} list";
write_log "[.] User ${username} already in the ${ssh_forward_group} list" "1";
continue;
fi;
# try to find user in user_list.txt and get the allow/forward flag from there,
@@ -88,9 +114,9 @@ for username in "$@"; do
# if not valid use allow
ssh_add_group="${SSH_GROUP_ADD}";
if [ -z "${SSH_GROUP_ADD}" ] && [ -f "${root_folder}${input_file}" ]; then
ssh_add_group=$(grep "${username}" "${root_folder}${input_file}" | cut -d ";" -f 4 | tr A-Z a-z | tr -d ' ');
ssh_add_group=$(grep "${username}" "${root_folder}${input_file}" | cut -d ";" -f 4 | tr '[:upper]' '[:lower:]' | tr -d ' ');
fi;
if [ "${ssh_access_type}" != "allow" ] && [ "${ssh_access_type}" != "forward" ]; then
if [ "${ssh_add_group}" != "allow" ] && [ "${ssh_add_group}" != "forward" ]; then
ssh_add_group="allow";
fi;
ssh_add_group="ssh${ssh_add_group}";
@@ -98,8 +124,9 @@ for username in "$@"; do
# check if user is in reject group remove
if id -nGz "${username}" | grep -qzxF "${ssh_reject_group}"; then
# remove user from ssh group and add to reject groups
echo "[*] User ${username} will be added to ${ssh_add_group}";
write_log "[*] User ${username} will be added to ${ssh_add_group}" "1";
if [ ${TEST} -eq 1 ]; then
# shellcheck disable=SC2059
printf "${user_group_tpl}" "${username}" "${ssh_reject_group}" "${username}" "${ssh_add_group}";
else
gpasswd -d "${username}" "${ssh_reject_group}";
@@ -107,7 +134,7 @@ for username in "$@"; do
fi;
else
# skip not ssh user
echo "[?] User ${username} not in the ssh reject group";
write_log "[?] User ${username} not in the ssh reject group" "1";
fi;
done;

View File

@@ -1 +1 @@
#user_id;user_name;group,subgroup;ssh access type;override password;override hostname;override ssh type
#user_id;user_name;group,subgroup;ssh access type|no login flag;override password;override hostname;override ssh type