Files
ServerUserCreate/bin/create_user.sh
2024-09-04 14:04:25 +09:00

469 lines
15 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 [ ! -z "${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;
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;
# 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
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} \\"
echo "${input_file} \\"
echo "${output_file} \\"
echo "${SSH_KEYGEN_FOLDER} \\"
echo "-x\*.gitignore;"
echo "Download: ${ROOT_FOLDER}${output_zip_folder}${output_zip}";
fi;
# cleam up user log file and ssh keys
if [ ${TEST} -eq 0 ]; then
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;
# __END__