456 lines
14 KiB
Bash
Executable File
456 lines
14 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
|
|
# * 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]
|
|
# 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)
|
|
# * 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]
|
|
#
|
|
# * PEM KEY
|
|
# <hostname>#<group>#<user>#<ssh key type>.pem
|
|
# * PUBLIC KEY
|
|
# <hostname>#<group>#<user>#<ssh key type>.pem.pub
|
|
# stored as zip in
|
|
# zip/
|
|
#
|
|
# If a previously exsting PEM key should be used, put the public pem file
|
|
# into the ssh-keygen/ folder
|
|
# They pem pub key must follow the set rules above
|
|
|
|
# SET TO 1 to TEST [will not create user/group/folder]
|
|
TEST=0; # no actions will be run
|
|
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)
|
|
GO=1;
|
|
;;
|
|
t|test)
|
|
TEST=1;
|
|
;;
|
|
i|info)
|
|
INFO=1;
|
|
;;
|
|
h|home)
|
|
HOME_LOCATION="${OPTARG}";
|
|
;;
|
|
\?)
|
|
echo -e "\n Option does not exist: ${OPTARG}\n";
|
|
echo "Use -t for test and -i for info";
|
|
echo "Use -g for actually creation run";
|
|
echo "Override default /home/ folder location with -h <base>";
|
|
exit 1;
|
|
;;
|
|
esac;
|
|
done;
|
|
error=0;
|
|
# hostname for output file only
|
|
host=$(hostname);
|
|
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))"/";
|
|
# home folder is always thome
|
|
HOME_BASE="/home/";
|
|
# config location
|
|
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')
|
|
fi;
|
|
|
|
if [ ! -z "${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}";
|
|
error=1;
|
|
fi;
|
|
# must be valid folder
|
|
if [ ! -d "${HOME_LOCATION}" ]; then
|
|
echo "Folder for home location does not exists: ${HOME_LOCATION}";
|
|
error=1;
|
|
fi;
|
|
fi;
|
|
# the new location for home, if override is set will be created in this folder
|
|
HOME_FOLDER="${HOME_LOCATION}${HOME_BASE}"
|
|
if [ ! -d "${HOME_FOLDER}" ]; then
|
|
echo "Home folder location not found: ${HOME_FOLDER}";
|
|
error=1;
|
|
fi;
|
|
# allow 10 to 39 length for password
|
|
if ! [[ "${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;
|
|
error=1;
|
|
# home dir error abort
|
|
if [ $error -eq 1 ]; then
|
|
exit;
|
|
fi;
|
|
ROOT_FOLDER="${BASE_FOLDER}../";
|
|
input_file='user_list.txt';
|
|
output_file="user_password.${timestamp}.txt";
|
|
output_zip_folder='zip/';
|
|
output_zip="users.${timestamp}.zip"
|
|
SSH_KEYGEN_FOLDER='ssh-keygen/';
|
|
SSH_KEYGEN_FOLDER_CREATED_PUB='ssh-keygen-created-pub/';
|
|
# set default key tpye
|
|
default_ssh_keytype='ed25519';
|
|
ssh_keytype='';
|
|
# sshallow or sshforward
|
|
ssh_group='';
|
|
ssh_forward_ok=0;
|
|
# 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//');
|
|
if [ ! -d "${SSH_CENTRAL_AUTHORIZED_FILE_FOLDER}" ]; then
|
|
echo "ssh central authorized_file folder could not be found: ${SSH_CENTRAL_AUTHORIZED_FILE_FOLDER}";
|
|
exit;
|
|
fi;
|
|
fi;
|
|
done;
|
|
|
|
# check if ssh key folder exists
|
|
if [ ! -d "${ROOT_FOLDER}${SSH_KEYGEN_FOLDER}" ]; then
|
|
mkdir "${ROOT_FOLDER}${SSH_KEYGEN_FOLDER}";
|
|
fi;
|
|
# check if zip folder is missing
|
|
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
|
|
echo "Missing pwgen application, aborting";
|
|
error=1;
|
|
fi;
|
|
# check for zip
|
|
# if [ ! command -v zip &> /dev/null ]; 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
|
|
echo "Missing ssh access group: sshallow";
|
|
error=1;
|
|
fi;
|
|
# flag if we can set ssh forward
|
|
if [ ! -z $(cat /etc/group | grep "sshforward:") ]; then
|
|
ssh_forward_ok=1;
|
|
fi;
|
|
# check if user list file exists
|
|
if [ ! -f "${ROOT_FOLDER}${input_file}" ]; then
|
|
echo "Missing ${ROOT_FOLDER}${input_file}";
|
|
error=1;
|
|
fi;
|
|
# make sure my own folder is owned by root and 600 (except for testing)
|
|
if [ $(stat -c %a .) != "600" ]; then
|
|
echo "!!!! RECOMMENDED TO HAVE BASE FOLDER SET TO '600' AND USER 'root' !!!!"
|
|
fi;
|
|
if [ $(whoami) != "root" ]; then
|
|
if [ ${TEST} -eq 0 ] && [ ${INFO} -eq 0 ]; then
|
|
echo "Script must be run as root user";
|
|
error=1;
|
|
else
|
|
echo "!!!! Script must be run as root user !!!!";
|
|
fi;
|
|
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.";
|
|
echo "It is recommended to run -t for testing prior to user creation.";
|
|
error=1;
|
|
fi;
|
|
|
|
if [ $error -eq 1 ]; then
|
|
exit;
|
|
fi;
|
|
|
|
# create users
|
|
cat "${ROOT_FOLDER}${input_file}" |
|
|
while read i; do
|
|
# skip rows start with # (comment)
|
|
if [[ "${i}" =~ ^\# ]]; then
|
|
continue;
|
|
fi;
|
|
# POS 2: make lower case, remove spaces
|
|
username=$(echo "${i}" | cut -d ";" -f 2 | tr A-Z a-z | 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;
|
|
fi;
|
|
# POS 3: groups
|
|
_group=$(echo "${i}" | cut -d ";" -f 3 | tr A-Z a-z | 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 ' ');
|
|
# 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";
|
|
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;
|
|
fi;
|
|
ssh_group="ssh${ssh_access_type}";
|
|
# sshallow group is always added
|
|
sub_group_opt=" -G ${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}";
|
|
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 ' ');
|
|
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 ' ');
|
|
if [ "${_ssh_keytype}" = "rsa" ]; then
|
|
ssh_keytype="${_ssh_keytype}";
|
|
#echo "[!!] BACKWARDS COMPATIBLE RSA TYPE SELECTION [!!]";
|
|
else
|
|
ssh_keytype=${default_ssh_keytype};
|
|
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;
|
|
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}";
|
|
group_error=1;
|
|
fi;
|
|
done;
|
|
if [ $group_error -eq 1 ] && [ ${TEST} -eq 0 ]; then
|
|
break;
|
|
fi;
|
|
fi;
|
|
# SSH file name part without folder
|
|
ssh_keygen_id="${hostname}${separator}${group}${separator}${username}${separator}${ssh_keytype}.pem";
|
|
# the full file including folder name
|
|
ssh_keyfile="${ROOT_FOLDER}${SSH_KEYGEN_FOLDER}${ssh_keygen_id}";
|
|
# publ file if new
|
|
ssh_keyfile_pub="${ssh_keyfile}.pub";
|
|
# check existing pub file
|
|
ssh_keyfile_check_pub="${ROOT_FOLDER}${SSH_KEYGEN_FOLDER_CREATED_PUB}${ssh_keygen_id}.pub";
|
|
|
|
if [ ${INFO} -eq 1 ]; then
|
|
# 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";
|
|
fi;
|
|
if [ -f "${ssh_keyfile_check_pub}" ]; then
|
|
echo -n ", SSH Pub key OK";
|
|
fi;
|
|
# line break
|
|
echo "";
|
|
continue;
|
|
fi;
|
|
|
|
# add group for each entry in _group
|
|
for create_group in ${_group//,/ }; do
|
|
if [ ${TEST} -eq 0 ]; then
|
|
groupadd -f ${create_group};
|
|
else
|
|
echo "$> groupadd -f ${create_group}";
|
|
fi;
|
|
done;
|
|
# 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})'";
|
|
else
|
|
echo "++ Create '${username}:${group}(${sub_group})'";
|
|
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};
|
|
else
|
|
echo "$> useradd -c `date +"%F"` -s /bin/bash -g ${group}${sub_group_opt} -d "${HOME_FOLDER}${username}" -m ${username}";
|
|
fi;
|
|
fi;
|
|
# set the auth file
|
|
if [ -z "${SSH_CENTRAL_AUTHORIZED_FILE_FOLDER}" ]; then
|
|
SSH_AUTHORIZED_FILE="${HOME_FOLDER}${username}/.ssh/authorized_keys";
|
|
else
|
|
SSH_AUTHORIZED_FILE="${SSH_CENTRAL_AUTHORIZED_FILE_FOLDER}${username}";
|
|
fi;
|
|
skip_ssh=0;
|
|
# if public pem already exists skip creation
|
|
if [ ! -f "${ssh_keyfile_check_pub}" ]; then
|
|
# Note we only create a password if we need it
|
|
# password + store pwgen 10 1 -1
|
|
if [ -z "${_password}" ]; then
|
|
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}'";
|
|
if [ ${TEST} -eq 0 ]; then
|
|
ssh-keygen \
|
|
-t ${ssh_keytype} \
|
|
-f "${ssh_keyfile}" \
|
|
-C "${hostname}: ${username}@${group}" \
|
|
-a 100 -N "${password}"
|
|
else
|
|
echo "$> ssh-keygen -t ${ssh_keytype} -f ${ssh_keyfile} -C ${hostname}: ${username}@${group} -a 100 -N ${password}";
|
|
fi;
|
|
else
|
|
found='';
|
|
if [ -f "${SSH_AUTHORIZED_FILE}" ]; then
|
|
found=$(grep "$(cat ${ssh_keyfile_check_pub})" ${SSH_AUTHORIZED_FILE});
|
|
fi;
|
|
if [ ! -z "${found}" ]; then
|
|
skip_ssh=1;
|
|
echo "-- Skip SSH Key creation: ${ssh_keygen_id}.pub";
|
|
else
|
|
# override previously set with stored one
|
|
ssh_keyfile_pub=${ssh_keyfile_check_pub};
|
|
echo " < Use existing public ssh key '${ssh_keygen_id}.pub'";
|
|
# Password already set notification
|
|
fi;
|
|
password="[ALREADY SET]";
|
|
fi;
|
|
if [ ${skip_ssh} -eq 0 ]; then
|
|
# 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};
|
|
# 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/;
|
|
else
|
|
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}" ] &&
|
|
[ -f "${SSH_AUTHORIZED_FILE}" ];
|
|
then
|
|
chattr -i ${SSH_AUTHORIZED_FILE};
|
|
fi;
|
|
cat "${ssh_keyfile_pub}" > ${SSH_AUTHORIZED_FILE};
|
|
else
|
|
if
|
|
[ ! -z "${SSH_CENTRAL_AUTHORIZED_FILE_FOLDER}" ] &&
|
|
[ -f "${SSH_AUTHORIZED_FILE}" ];
|
|
then
|
|
echo "$> chattr -i ${SSH_AUTHORIZED_FILE}";
|
|
fi;
|
|
echo "$> cat ${ssh_keyfile_pub} > ${SSH_AUTHORIZED_FILE}";
|
|
fi;
|
|
# secure
|
|
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};
|
|
else
|
|
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};
|
|
# set +i so user can't change 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}";
|
|
fi;
|
|
fi;
|
|
fi;
|
|
done;
|
|
|
|
# End before anything because this is just info run
|
|
if [ ${INFO} -eq 1 ]; then
|
|
exit;
|
|
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;
|
|
else
|
|
echo "zip -r \\"
|
|
echo "${ROOT_FOLDER}${output_zip_folder}${output_zip} \\"
|
|
echo "${input_file} \\"
|
|
echo "${output_file} \\"
|
|
echo "${SSH_KEYGEN_FOLDER} \\"
|
|
echo "-x\*.gitignore;"
|
|
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}"*;
|
|
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;
|
|
|
|
# __END__
|