Compare commits

..

47 Commits

Author SHA1 Message Date
Clemens Schwaighofer
e45b89c582 AWS Delete user, remove all secondary groups first.
To make sure tha on delete the user is removed from all secondary groups
unset them first before running the userdel command.

-r might not be enought to do that in some situations
2025-01-06 13:45:51 +09:00
Clemens Schwaighofer
4a8dab7b01 Add base folder for lock user aws script 2024-12-16 15:44:09 +09:00
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
Clemens Schwaighofer
8fb833d3c4 Fix the call for getting instance data
Do not use name alias, use the IP address for this
2023-12-22 13:31:33 +09:00
Clemens Schwaighofer
694f04313c Add Account, Region and Instance info for each report
So we can easy match up user reports to other information we collect
2023-12-22 11:54:34 +09:00
Clemens Schwaighofer
50e28c7cfd Check that fallback last access file exists 2023-12-21 16:43:57 +09:00
Clemens Schwaighofer
65b7a6ad43 Fix date check for account created date
Use regex to check instead of empty string.
A comment could be set instead of a date
2023-12-21 16:39:05 +09:00
Clemens Schwaighofer
244461d466 sshforward group typo in lock and unlock users 2023-12-21 16:11:31 +09:00
Clemens Schwaighofer
66213dfd65 Text fixes for check last login 2023-12-21 16:06:53 +09:00
Clemens Schwaighofer
39da44b546 Wrong unlock account var used for unlock users 2023-12-21 16:01:35 +09:00
Clemens Schwaighofer
d4bb06e3e1 Fix lock user flow to only lock if the user is not in the reject group 2023-12-21 15:58:28 +09:00
Clemens Schwaighofer
cc647de495 Readme update 2023-12-21 15:14:57 +09:00
Clemens Schwaighofer
68b450baaf Add warning message for logins 2023-12-21 13:46:58 +09:00
Clemens Schwaighofer
8452a1b8c0 Fix pre check for ssh login checks 2023-12-21 13:35:50 +09:00
Clemens Schwaighofer
3fcb74ac47 Update check last login script with better reporting and csv/json output
Now for each ssh group we report last login/account create stats.
Add the main user group to output
Add unlock user commands for locked users

Add CSV and JSON formatted output
2023-12-21 13:23:35 +09:00
Clemens Schwaighofer
70212da3cb systemd logs end with dot and we split, so we missed user names with . inside 2023-12-20 17:51:35 +09:00
11 changed files with 818 additions and 288 deletions

2
.shellcheckrc Normal file
View File

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

103
Readme.md
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;;;
...
```
@@ -257,6 +291,61 @@ 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",
"group_a",
"group_b",
],
"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 <user 1> <user 2> ...`

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,131 +4,325 @@
# 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');
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
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";
error=1;
fi;
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;
if [ $error -eq 1 ]; then
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";
for ssh_group in ${ssh_groups[@]}; do
echo "--------------------->"
echo "Checking Group : ${ssh_group}";
for username in $(cat /etc/group|grep "${ssh_group}:" | cut -d ":" -f 4 | sed -e 's/,/ /g'); do
# 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://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") &&
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)
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";
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
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;
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
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)
# shellcheck disable=SC2059
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=$(grep "${username}:" /etc/passwd | 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=$(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}");
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
# 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="";
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;
if [ ! -z "${found}" ]; then
last_login_date=$(grep "${username};" "${AUTH_LOG}" | cut -d ";" -f 2 | date +"%s" -f -);
# always pre work account dates if they exist, but output only if text
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
#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 [ -n "${found}" ]; then
last_login_date_string=$(grep "${username};" "${AUTH_LOG}" | cut -d ";" -f 2);
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";
lock_user=1;
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="WARN [last ssh login ${last_login} days ago]";
else
out_string="OK [ssh]";
out_string="OK [ssh, ${last_login} days ago]";
fi;
elif [ ! -z "${last_login_string##*$search*}" ]; then
login_source="ssh";
# rewrite to Y-M-D, aka
last_login_date="${last_login_date_string}"
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";
lock_user=1;
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="WARN [last terminal login ${last_login} days ago]";
else
out_string="OK [lastlog]";
out_string="OK [lastlog, ${last_login} days ago]";
fi;
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;
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 [ -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 [first login]";
out_string="OK [Never logged in, created ${account_age} days ago]";
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}";
done;
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)
# 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 <<< "$(grep "${ssh_group}:" /etc/group | cut -d ":" -f 4 | sed -e 's/,/\n/g')";
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 [ -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 [ -n "${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__

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}";
@@ -61,7 +67,8 @@ function parseLog()
if [ "${logger}" = "systemd" ]; then
# 2022-11-18T20:04:08+0900
auth_date=$(echo "${line}" | cut -d " " -f 1);
auth_user=$(echo "${line}" | cut -d "]" -f 2 | cut -d " " -f 7 | cut -d "." -f 1);
# Note, instead of cut with dot, remove last dot in line
auth_user=$(echo "${line}" | cut -d "]" -f 2 | cut -d " " -f 7 | sed -e "s/\.$//");
else
auth_date=$(echo "${line}" | cut -c 1-6)" ${start_year} "$(echo "${line}" | cut -c 8-15);
auth_user=$(echo "${line}" | cut -d ")" -f 2 | cut -d " " -f 6 | cut -d "(" -f 1);
@@ -71,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
@@ -102,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
@@ -119,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;
@@ -131,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,9 +164,13 @@ for username in "$@"; do
echo "[1] Remove user + home dir";
if [ ${TEST} -eq 0 ]; then
userdel -r ${username}
# remove all secondary group entries first before we delete the user
# there might be cases where they are left
usermod -G "" "${username}";
userdel -r "${username}";
else
echo "$> userdel -r ${username}";
echo "$> usermod -G \"\" \"${username}\"";
echo "$> userdel -r \"${username}\"";
fi;
# remove ssh files in pub
@@ -162,7 +186,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 +196,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,35 +38,58 @@ 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;
ssh_allow_group="sshallow";
ssh_forward_group="sshfoward";
ssh_forward_group="sshforward";
user_group_tpl="gpasswd -d %s %s\ngpasswd -a %s %s\n";
# base folder for all data
BASE_FOLDER=$(dirname "$(readlink -f "$0")")"/";
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 +99,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 +117,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,51 +41,72 @@ 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;
ssh_allow_group="sshallow";
ssh_forward_group="sshfoward";
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