From be57c15186e5fa02994804f803242dc4d4fe3991 Mon Sep 17 00:00:00 2001 From: Pratik Date: Wed, 13 Feb 2019 05:15:50 +0530 Subject: [PATCH] Refactored to achieve the Better readability - Renamed function names to be more consistent - Renamed function names to mean what thet do - Renamed variables to mean what they do The op_code variable was confusing and would have created bugs - Refined use of op_code and exit_code - Need full tests to be done all over again --- init-linux-harden.sh | 1100 +++++++++++++++++++++--------------------- 1 file changed, 558 insertions(+), 542 deletions(-) diff --git a/init-linux-harden.sh b/init-linux-harden.sh index 39f2526..84e1b8c 100644 --- a/init-linux-harden.sh +++ b/init-linux-harden.sh @@ -226,443 +226,7 @@ fi ############################################################## -# Op-tech & Utility Functions -############################################################## - -OP_CODE=0 -# keep state of the script -# Each step has 3 states - # 0 - not executed - # 1 - Started - # 2 - Completed - # 3 - Failed -CreateNonRootUser=0 -CreateSSHKey=0 -SecureAuthkeysfile=0 -ChangeSourceList=0 -InstallReqSoftwares=0 -ConfigureUFW=0 -ConfigureFail2Ban=0 -ChangeRootPwd=0 -ScheduleUpdate=0 -EnableSSHOnly=0 - -OP_TEXT=( - "Creating new user" #0 - "Creating SSH Key for new user" #1 - "Securing 'authorized_keys' file" #2 - "Enabling SSH-only login" #3 - "Reset sources.list to defaults" #4 - "Installing required softwares" #5 - "Configure UFW" #6 - "Configure Fail2Ban" #7 - "Changing root password" #8 - "Scheduling daily update download" #9 -) - -function set_op_code() { - if [[ $OP_CODE -eq 0 ]] && [[ $1 -gt 0 ]]; then - OP_CODE=$1 - fi -} - -function reset_op_code(){ - OP_CODE=0 -} - -function update_event_status() { - local event - event=$(get_event_var_from_event "$1") - eval "$event"="$2" -} - -function get_event_status() { - local event=get_event_var_from_event "$1" - return ${!event} -} - -function get_event_var_from_event() { - case $1 in - "${OP_TEXT[0]}") - echo "CreateNonRootUser" - ;; - "${OP_TEXT[1]}") - echo "CreateSSHKey" - ;; - "${OP_TEXT[2]}") - echo "SecureAuthkeysfile" - ;; - "${OP_TEXT[3]}") - echo "EnableSSHOnly" - ;; - "${OP_TEXT[4]}") - echo "ChangeSourceList" - ;; - "${OP_TEXT[5]}") - echo "InstallReqSoftwares" - ;; - "${OP_TEXT[6]}") - echo "ConfigureUFW" - ;; - "${OP_TEXT[7]}") - echo "ConfigureFail2Ban" - ;; - "${OP_TEXT[8]}") - echo "ChangeRootPwd" - ;; - "${OP_TEXT[9]}") - echo "ScheduleUpdate" - ;; - *) - false - ;; - esac -} - -function service_action_and_chk_error() { - local servicename=$1 - local serviceaction=$2 - local servicemsg - - servicemsg=$(service "$servicename" "$serviceaction" 2>&1) - file_log "$servicemsg" - return $(echo "$servicemsg" | grep -c 'ERROR') -} - -function finally(){ - if [[ $CreateNonRootUser -eq 2 ]] && - [[ $CreateSSHKey -eq 2 ]] && - [[ $SecureAuthkeysfile -eq 2 ]] && - [[ $ChangeSourceList -le 2 ]] && # Since 0 (NO-OP) is still success - [[ $InstallReqSoftwares -eq 2 ]] && - [[ $ConfigureUFW -le 2 ]] && # Since 0 (NO-OP) is still success - [[ $ConfigureFail2Ban -le 2 ]] && # Since 0 (NO-OP) is still success - [[ $ScheduleUpdate -eq 2 ]] && - [[ $EnableSSHOnly -eq 2 ]]; then - echo - line_fill "$CHORIZONTAL" "$CLINESIZE" - line_fill "$CHORIZONTAL" "$CLINESIZE" - center_reg_text "ALL OPERATIONS COMPLETED SUCCESSFULLY" - fi - - #Recap - file_log "" - file_log "" - file_log "" - file_log "" - - line_fill "$CHORIZONTAL" "$CLINESIZE" - recap "User Name" "$CreateNonRootUser" "$NORM_USER_NAME" - recap "User's Password" "$CreateNonRootUser" "$USER_PASS" - recap "SSH Private Key File" "$CreateSSHKey" "$SSH_DIR"/"$NORM_USER_NAME".pem - recap "SSH Public Key File" "$CreateSSHKey" "$SSH_DIR"/"$NORM_USER_NAME".pem.pub - recap "SSH Key Passphrase" "$CreateSSHKey" "$KEY_PASS" - if [[ "$RESET_ROOT_PWD" == "y" ]]; then - recap "New root Password" "$ChangeRootPwd" "$PASS_ROOT" - fi - line_fill "$CHORIZONTAL" "$CLINESIZE" - - recap_file_content "SSH Private Key" "$SSH_DIR"/"$NORM_USER_NAME".pem - recap_file_content "SSH Public Key" "$SSH_DIR"/"$NORM_USER_NAME".pem.pub - - line_fill "$CHORIZONTAL" "$CLINESIZE" - center_reg_text "!!! DO NOT LOG OUT JUST YET !!!" - center_reg_text "Use another window to test out the above credentials" - center_reg_text "If you face issue logging in look at the log file to see what went wrong" - center_reg_text "Log file at ${LOGFILE}" - - line_fill "$CHORIZONTAL" "$CLINESIZE" - echo - - if [[ $ChangeSourceList -eq 3 ]] || - [[ $InstallReqSoftwares -eq 3 ]] || - [[ $ConfigureUFW -eq 3 ]] || - [[ $ConfigureFail2Ban -eq 3 ]] - [[ $ScheduleUpdate -eq 3 ]] && - [[ $ChangeRootPwd -eq 3 ]]; then - center_err_text "Some operations failed..." - center_err_text "These may NOT be catastrophic" - center_err_text "Please check $LOGFILE file for details" - echo - fi - - if [[ $HIDE_CREDENTIALS == "y" ]]; then - center_reg_text "Issue the following command to see all credentials" - center_reg_text "tail -n 20 ${LOGFILE}" - fi -} - - -############################################################## -# Error Handling -############################################################## - -function revert_changes(){ - file_log "Starting revert operation..." - - if [[ $1 = "${OP_TEXT[0]}" ]]; then - revert_create_user - elif [[ $1 = "${OP_TEXT[1]}" ]]; then - revert_create_ssh_key - elif [[ $1 = "${OP_TEXT[2]}" ]]; then - revert_secure_authorized_key - elif [[ $1 = "${OP_TEXT[3]}" ]]; then - revert_ssh_only_login - elif [[ $1 = "${OP_TEXT[4]}" ]]; then - # This can be reverted back individually - revert_source_list_changes - elif [[ $1 = "${OP_TEXT[5]}" ]]; then - # This can be reverted back individually - return 7 - fi -} - -function error_restoring(){ - op_log "$1" "FAILED" - file_log "$1 - Failed" - echo - center_err_text "!!! Error restoring changes !!!" - center_err_text "!!! You may have to manually fix this !!!" - center_err_text "!!! Check the log file for details !!!" - center_reg_text "Log file at ${LOGFILE}" - echo -} - -function revert_create_user(){ - file_log "Reverting New User Creation..." - - # Remove user and its home directory only if user was created - if [[ $(getent passwd "$NORM_USER_NAME" | wc -l) -gt 0 ]]; then - { - file_log "Deleting user ${NORM_USER_NAME} ..." - deluser "$NORM_USER_NAME" - - file_log "Deleting user ${NORM_USER_NAME} home directory and all its content ..." - rm -rf /home/"${NORM_USER_NAME:?}" - set_op_code $? - } 2>> "$LOGFILE" >&2 - set_op_code $? - fi - - if [[ $OP_CODE -eq 0 ]]; then - op_rev_log "Reverting - New User Creation" "SUCCESSFUL" - else - error_restoring "Reverting - New User Creation" - fi - - reset_op_code -} - -function revert_create_ssh_key(){ - file_log "Reverting SSH Key Generation..." - revert_create_user - set_op_code $? - - # Since all SSH files are created inside the user's home directory - # There is nothing to revert if username is deleted - - if [[ $OP_CODE -eq 0 ]]; then - op_rev_log "Reverting - SSH Key Generation" "SUCCESSFUL" - else - error_restoring "Reverting - SSH Key Generation" - fi - - reset_op_code -} - -function revert_secure_authorized_key(){ - file_log "Reverting SSH Key Authorizations..." - - if [[ -f "$SSH_DIR"/authorized_keys ]]; then - { - file_log "Removing the immutable flag from every file in /home/${NORM_USER_NAME}/.ssh/ directory ..." - chattr -i "$SSH_DIR"/* - set_op_code $? - - # Nothing else to restore since we are going to delete the user & its directories anyways - # All the files created/changed by the script are inside /home/[username]/.ssh directory only - } 2>> "$LOGFILE" >&2 - fi - - # Can remove the user only AFTER immutable attributes on authorized_keys is removed - revert_create_ssh_key - - if [[ $OP_CODE -eq 0 ]]; then - op_rev_log "Reverting - SSH Key Authorization" "SUCCESSFUL" - else - error_restoring "Reverting - SSH Key Authorization" - fi - - reset_op_code -} - -function revert_source_list_changes(){ - file_log "Reverting Source_list Changes..." - - unalias cp &>/dev/null - - if [[ -f /etc/apt/sources.list"${BACKUP_EXTENSION}" ]]; then - file_log "Restoring /etc/apt/sources.list${BACKUP_EXTENSION} into /etc/apt/sources.list ..." - cp -rf /etc/apt/sources.list"${BACKUP_EXTENSION}" /etc/apt/sources.list 2>> "$LOGFILE" >&2 - set_op_code $? - fi - - SOURCE_FILES_BKP=(/etc/apt/source*/*.list"$BACKUP_EXTENSION") - if [[ ${#SOURCE_FILES_BKP[@]} -gt 0 ]]; then - for file in "${SOURCE_FILES_BKP[@]}"; - do - file_log "Restoring ${file} into ${file//$BACKUP_EXTENSION/} ..." - cp -rf "$file" "${file//$BACKUP_EXTENSION/}" 2>> "$LOGFILE" >&2 - set_op_code $? - done - fi - - if [[ $OP_CODE -eq 0 ]]; then - op_rev_log "Reverting - Source_list Changes" "SUCCESSFUL" - else - error_restoring "Reverting - Source_list Changes" - fi - - reset_op_code -} - -function revert_root_pass_change(){ - echo - center_err_text "Changing root password failed..." - center_err_text "Your earlier root password remains VALID" - center_err_text "Script will continue to next step" -} - -function revert_config_UFW(){ - file_log "Reverting UFW Configuration..." - - ufw disable 2>> "$LOGFILE" >&2 - set_op_code $? - - if [[ $OP_CODE -eq 0 ]]; then - op_rev_log "Reverting - UFW Configuration" "SUCCESSFUL" - else - error_restoring "Reverting - UFW Configuration" - fi - - reset_op_code -} - -function revert_config_fail2ban(){ - file_log "Reverting Fail2ban Config..." - - unalias cp &>/dev/null - - if [[ -f /etc/fail2ban/jail.local"$BACKUP_EXTENSION" ]]; then - # If /etc/fail2ban/jail.local/_bkp exists then this is NOT the 1st time script is run - # So, you would probaly want to get the last existing jail.local file back - file_log "Restoring /etc/fail2ban/jail.local${BACKUP_EXTENSION} into /etc/fail2ban/jail.local" - cp -rf /etc/fail2ban/jail.local"$BACKUP_EXTENSION" /etc/fail2ban/jail.local 2>> "$LOGFILE" >&2 - set_op_code $? - else - # If /etc/fail2ban/jail.local/_bkp does NOT exists then this IS the 1st time script is run - # You probably do NOT want the jail.local > which might be corrupted > which is why you are here - file_log "Removing /etc/fail2ban/jail.local as that might have been the culprit in this failure" - rm /etc/fail2ban/jail.local 2>> "$LOGFILE" >&2 - set_op_code $? - fi - - if [[ -f /etc/fail2ban/jail.d/defaults-debian.conf"$BACKUP_EXTENSION" ]]; then - file_log "Restoring /etc/fail2ban/jail.d/defaults-debian.conf${BACKUP_EXTENSION} into /etc/fail2ban/jail.d/defaults-debian.conf" - cp -rf /etc/fail2ban/jail.d/defaults-debian.conf"$BACKUP_EXTENSION" /etc/fail2ban/jail.d/defaults-debian.conf 2>> "$LOGFILE" >&2 - set_op_code $? - fi - - file_log "Stopping fail2ban service ..." - { - set_op_code $(service_action_and_chk_error "fail2ban" "stop") - } 2>> "$LOGFILE" >&2 - - if [[ $OP_CODE -eq 0 ]]; then - op_rev_log "Reverting - Fail2ban Config" "SUCCESSFUL" - else - error_restoring "Reverting - Fail2ban Config" - fi - - reset_op_code -} - -function revert_software_installs(){ - echo - center_err_text "Error while installing softwares" - center_err_text "This may be a false-alarm" - center_err_text "Script will continue to next step" - file_log "Installing software failed..." - file_log "This is NOT a catastrophic error" -} - -function revert_schedule_updates() { - file_log "Reverting Daily Update Download..." - - rm $dailycron_filename - set_op_code $? - - if [[ $OP_CODE -eq 0 ]]; then - op_rev_log "Reverting - Daily Update Download" "SUCCESSFUL" - else - error_restoring "Reverting - Daily Update Download" - fi - - reset_op_code -} - -function revert_ssh_only_login(){ - revert_secure_authorized_key - if [[ $DEFAULT_SOURCE_LIST = "y" ]]; then - revert_source_list_changes - fi - revert_config_UFW - revert_config_fail2ban - revert_schedule_updates - - file_log "Reverting SSH-only Login..." - - if [[ -f /etc/ssh/sshd_config"$BACKUP_EXTENSION" ]]; then - unalias cp &>/dev/null - - file_log "Restoring /etc/ssh/sshd_config${BACKUP_EXTENSION} into /etc/ssh/sshd_config ..." - cp -rf /etc/ssh/sshd_config"$BACKUP_EXTENSION" /etc/ssh/sshd_config 2>> "$LOGFILE" >&2 - set_op_code $? - fi - - file_log "Restarting ssh service ..." - { - set_op_code $(service_action_and_chk_error "sshd" "restart") - - if [[ $OP_CODE -eq 0 ]]; then - false - fi - } || { - # Because Ubuntu 14.04 does not have sshd - set_op_code $(service_action_and_chk_error "ssh" "restart") - } 2>> "$LOGFILE" >&2 - - if [[ $OP_CODE -eq 0 ]]; then - op_rev_log "Reverting - SSH-only Login" "SUCCESSFUL" - else - error_restoring "Reverting - SSH-only Login" - fi -} - -function revert_everything_and_exit() { - echo - center_err_text "!!! ERROR OCCURED DURING OPERATION !!!" - center_err_text "!!! Reverting changes !!!" - center_err_text "Please look at $LOGFILE for details" - echo - revert_changes "$1" - - exit 1; -} - - -############################################################## -# Log +# Log - Cosmetics ############################################################## CVERTICAL="|" @@ -700,11 +264,16 @@ function line_fill() { printf "\\n" } + +############################################################## +# Op Logs +############################################################## + function file_log(){ printf "%s - %s\\n" "$(date '+%d-%b-%Y %H:%M:%S')" "$1" >> "$LOGFILE" } -function op_log() { +function log_step_status() { local EVENT=$1 local RESULT=$2 @@ -723,13 +292,13 @@ function op_log() { fi } -function op_rev_log(){ +function log_op_rev_status(){ printf "${CRED}" - op_log "$1" "$2" + log_step_status "$1" "$2" printf "${CEND}" } -function recap (){ +function log_ops_finish (){ local purpose=$1 local status=$2 local value=$3 @@ -752,7 +321,7 @@ function recap (){ fi } -function recap_file_content(){ +function log_ops_finish_file_contents() { local file_type=$1 local file_location=$2 @@ -769,29 +338,476 @@ function recap_file_content(){ fi } +function log_revert_error(){ + log_step_status "$1" "FAILED" + file_log "$1 - Failed" + echo + center_err_text "!!! Error restoring changes !!!" + center_err_text "!!! You may have to manually fix this !!!" + center_err_text "!!! Check the log file for details !!!" + center_reg_text "Log file at ${LOGFILE}" + echo +} + + +############################################################## +# Op Error Handling +############################################################## + +function revert_changes(){ + file_log "Starting revert operation..." + + if [[ $1 = "${STEP_TEXT[0]}" ]]; then + revert_create_user + elif [[ $1 = "${STEP_TEXT[1]}" ]]; then + revert_create_ssh_key + elif [[ $1 = "${STEP_TEXT[2]}" ]]; then + revert_secure_authorized_key + elif [[ $1 = "${STEP_TEXT[3]}" ]]; then + revert_ssh_only_login + elif [[ $1 = "${STEP_TEXT[4]}" ]]; then + # This can be reverted back individually + revert_source_list_changes + elif [[ $1 = "${STEP_TEXT[5]}" ]]; then + # This can be reverted back individually + return 7 + fi +} + +function revert_create_user(){ + file_log "Reverting New User Creation..." + + # Remove user and its home directory only if user was created + if [[ $(getent passwd "$NORM_USER_NAME" | wc -l) -gt 0 ]]; then + { + file_log "Deleting user ${NORM_USER_NAME} ..." + deluser "$NORM_USER_NAME" + + file_log "Deleting user ${NORM_USER_NAME} home directory and all its content ..." + rm -rf /home/"${NORM_USER_NAME:?}" + set_exit_code $? + } 2>> "$LOGFILE" >&2 + set_exit_code $? + fi + + if [[ $exit_code -eq 0 ]]; then + log_op_rev_status "Reverting - New User Creation" "SUCCESSFUL" + else + log_revert_error "Reverting - New User Creation" + fi + + reset_exit_code +} + +function revert_create_ssh_key(){ + file_log "Reverting SSH Key Generation..." + revert_create_user + set_exit_code $? + + # Since all SSH files are created inside the user's home directory + # There is nothing to revert if username is deleted + + if [[ $exit_code -eq 0 ]]; then + log_op_rev_status "Reverting - SSH Key Generation" "SUCCESSFUL" + else + log_revert_error "Reverting - SSH Key Generation" + fi + + reset_exit_code +} + +function revert_secure_authorized_key(){ + file_log "Reverting SSH Key Authorizations..." + + if [[ -f "$SSH_DIR"/authorized_keys ]]; then + { + file_log "Removing the immutable flag from every file in /home/${NORM_USER_NAME}/.ssh/ directory ..." + chattr -i "$SSH_DIR"/* + set_exit_code $? + + # Nothing else to restore since we are going to delete the user & its directories anyways + # All the files created/changed by the script are inside /home/[username]/.ssh directory only + } 2>> "$LOGFILE" >&2 + fi + + # Can remove the user only AFTER immutable attributes on authorized_keys is removed + revert_create_ssh_key + + if [[ $exit_code -eq 0 ]]; then + log_op_rev_status "Reverting - SSH Key Authorization" "SUCCESSFUL" + else + log_revert_error "Reverting - SSH Key Authorization" + fi + + reset_exit_code +} + +function revert_source_list_changes(){ + file_log "Reverting Source_list Changes..." + + unalias cp &>/dev/null + + if [[ -f /etc/apt/sources.list"${BACKUP_EXTENSION}" ]]; then + file_log "Restoring /etc/apt/sources.list${BACKUP_EXTENSION} into /etc/apt/sources.list ..." + cp -rf /etc/apt/sources.list"${BACKUP_EXTENSION}" /etc/apt/sources.list 2>> "$LOGFILE" >&2 + set_exit_code $? + fi + + SOURCE_FILES_BKP=(/etc/apt/source*/*.list"$BACKUP_EXTENSION") + if [[ ${#SOURCE_FILES_BKP[@]} -gt 0 ]]; then + for file in "${SOURCE_FILES_BKP[@]}"; + do + file_log "Restoring ${file} into ${file//$BACKUP_EXTENSION/} ..." + cp -rf "$file" "${file//$BACKUP_EXTENSION/}" 2>> "$LOGFILE" >&2 + set_exit_code $? + done + fi + + if [[ $exit_code -eq 0 ]]; then + log_op_rev_status "Reverting - Source_list Changes" "SUCCESSFUL" + else + log_revert_error "Reverting - Source_list Changes" + fi + + reset_exit_code +} + +function revert_root_pass_change(){ + echo + center_err_text "Changing root password failed..." + center_err_text "Your earlier root password remains VALID" + center_err_text "Script will continue to next step" +} + +function revert_config_UFW(){ + file_log "Reverting UFW Configuration..." + + ufw disable 2>> "$LOGFILE" >&2 + set_exit_code $? + + if [[ $exit_code -eq 0 ]]; then + op_rev_log "Reverting - UFW Configuration" "SUCCESSFUL" + else + log_revert_error "Reverting - UFW Configuration" + fi + + reset_exit_code +} + +function revert_config_fail2ban(){ + file_log "Reverting Fail2ban Config..." + + unalias cp &>/dev/null + + if [[ -f /etc/fail2ban/jail.local"$BACKUP_EXTENSION" ]]; then + # If /etc/fail2ban/jail.local/_bkp exists then this is NOT the 1st time script is run + # So, you would probaly want to get the last existing jail.local file back + file_log "Restoring /etc/fail2ban/jail.local${BACKUP_EXTENSION} into /etc/fail2ban/jail.local" + cp -rf /etc/fail2ban/jail.local"$BACKUP_EXTENSION" /etc/fail2ban/jail.local 2>> "$LOGFILE" >&2 + set_exit_code $? + else + # If /etc/fail2ban/jail.local/_bkp does NOT exists then this IS the 1st time script is run + # You probably do NOT want the jail.local > which might be corrupted > which is why you are here + file_log "Removing /etc/fail2ban/jail.local as that might have been the culprit in this failure" + rm /etc/fail2ban/jail.local 2>> "$LOGFILE" >&2 + set_exit_code $? + fi + + if [[ -f /etc/fail2ban/jail.d/defaults-debian.conf"$BACKUP_EXTENSION" ]]; then + file_log "Restoring /etc/fail2ban/jail.d/defaults-debian.conf${BACKUP_EXTENSION} into /etc/fail2ban/jail.d/defaults-debian.conf" + cp -rf /etc/fail2ban/jail.d/defaults-debian.conf"$BACKUP_EXTENSION" /etc/fail2ban/jail.d/defaults-debian.conf 2>> "$LOGFILE" >&2 + set_exit_code $? + fi + + file_log "Stopping fail2ban service ..." + { + set_exit_code $(service_action_and_chk_error "fail2ban" "stop") + } 2>> "$LOGFILE" >&2 + + if [[ $exit_code -eq 0 ]]; then + op_rev_log "Reverting - Fail2ban Config" "SUCCESSFUL" + else + log_revert_error "Reverting - Fail2ban Config" + fi + + reset_exit_code +} + +function revert_software_installs(){ + echo + center_err_text "Error while installing softwares" + center_err_text "This may be a false-alarm" + center_err_text "Script will continue to next step" + file_log "Installing software failed..." + file_log "This is NOT a catastrophic error" +} + +function revert_schedule_updates() { + file_log "Reverting Daily Update Download..." + + rm "$dailycron_filename" + set_exit_code $? + + if [[ $exit_code -eq 0 ]]; then + op_rev_log "Reverting - Daily Update Download" "SUCCESSFUL" + else + log_revert_error "Reverting - Daily Update Download" + fi + + reset_exit_code +} + +function revert_ssh_only_login(){ + revert_secure_authorized_key + if [[ $DEFAULT_SOURCE_LIST = "y" ]]; then + revert_source_list_changes + fi + revert_config_UFW + revert_config_fail2ban + revert_schedule_updates + + file_log "Reverting SSH-only Login..." + + if [[ -f /etc/ssh/sshd_config"$BACKUP_EXTENSION" ]]; then + unalias cp &>/dev/null + + file_log "Restoring /etc/ssh/sshd_config${BACKUP_EXTENSION} into /etc/ssh/sshd_config ..." + cp -rf /etc/ssh/sshd_config"$BACKUP_EXTENSION" /etc/ssh/sshd_config 2>> "$LOGFILE" >&2 + set_exit_code $? + fi + + file_log "Restarting ssh service ..." + { + set_exit_code $(service_action_and_chk_error "sshd" "restart") + + if [[ $exit_code -eq 0 ]]; then + false + fi + } || { + # Because Ubuntu 14.04 does not have sshd + set_exit_code $(service_action_and_chk_error "ssh" "restart") + } 2>> "$LOGFILE" >&2 + + if [[ $exit_code -eq 0 ]]; then + op_rev_log "Reverting - SSH-only Login" "SUCCESSFUL" + else + log_revert_error "Reverting - SSH-only Login" + fi +} + +function revert_everything_and_exit() { + echo + center_err_text "!!! ERROR OCCURED DURING OPERATION !!!" + center_err_text "!!! Reverting changes !!!" + center_err_text "Please look at $LOGFILE for details" + echo + revert_changes "$1" + + exit 1; +} + + +############################################################## +# Op-setup & Utility Functions +############################################################## +exit_code=0 + +# Step Status +# Each step has 1 of possible 3 states + # 0 - NO-OP - not executed + # 1 - STARTED - Started + # 2 - SUCCESSFUL - Successfully Completed + # 3 - FAILED - Completed with Error +CreateNonRootUser=0 +CreateSSHKey=0 +SecureAuthkeysfile=0 +ChangeSourceList=0 +InstallReqSoftwares=0 +ConfigureUFW=0 +ConfigureFail2Ban=0 +ChangeRootPwd=0 +ScheduleUpdate=0 +EnableSSHOnly=0 + +STEP_TEXT=( + "Creating new user" #0 + "Creating SSH Key for new user" #1 + "Securing 'authorized_keys' file" #2 + "Enabling SSH-only login" #3 + "Reset sources.list to defaults" #4 + "Installing required softwares" #5 + "Configure UFW" #6 + "Configure Fail2Ban" #7 + "Changing root password" #8 + "Scheduling daily update download" #9 +) + +function set_exit_code() { + if [[ $OP_CODE -eq 0 ]] && [[ $1 -gt 0 ]]; then + OP_CODE=$1 + fi +} + +function reset_exit_code() { + exit_code=0 +} + +function update_step_status() { + local event + event=$(get_step_var_from_stepname "$1") + eval "$event"="$2" +} + +function get_step_status() { + local event=get_step_var_from_stepname "$1" + return ${!event} +} + +function get_step_var_from_stepname() { + case $1 in + "${STEP_TEXT[0]}") + echo "CreateNonRootUser" + ;; + "${STEP_TEXT[1]}") + echo "CreateSSHKey" + ;; + "${STEP_TEXT[2]}") + echo "SecureAuthkeysfile" + ;; + "${STEP_TEXT[3]}") + echo "EnableSSHOnly" + ;; + "${STEP_TEXT[4]}") + echo "ChangeSourceList" + ;; + "${STEP_TEXT[5]}") + echo "InstallReqSoftwares" + ;; + "${STEP_TEXT[6]}") + echo "ConfigureUFW" + ;; + "${STEP_TEXT[7]}") + echo "ConfigureFail2Ban" + ;; + "${STEP_TEXT[8]}") + echo "ChangeRootPwd" + ;; + "${STEP_TEXT[9]}") + echo "ScheduleUpdate" + ;; + *) + false + ;; + esac +} + +function service_action_and_chk_error() { + local servicename=$1 + local serviceaction=$2 + local servicemsg + + servicemsg=$(service "$servicename" "$serviceaction" 2>&1) + file_log "$servicemsg" + return $(echo "$servicemsg" | grep -c 'ERROR') +} + +function recap() { + if [[ $CreateNonRootUser -eq 2 ]] && + [[ $CreateSSHKey -eq 2 ]] && + [[ $SecureAuthkeysfile -eq 2 ]] && + [[ $ChangeSourceList -le 2 ]] && # Since 0 (NO-OP) is still success + [[ $InstallReqSoftwares -eq 2 ]] && + [[ $ConfigureUFW -le 2 ]] && # Since 0 (NO-OP) is still success + [[ $ConfigureFail2Ban -le 2 ]] && # Since 0 (NO-OP) is still success + [[ $ScheduleUpdate -eq 2 ]] && + [[ $ChangeRootPwd -le 2 ]] && # Since 0 (NO-OP) is still success + [[ $EnableSSHOnly -eq 2 ]]; then + echo + line_fill "$CHORIZONTAL" "$CLINESIZE" + line_fill "$CHORIZONTAL" "$CLINESIZE" + center_reg_text "ALL OPERATIONS COMPLETED SUCCESSFULLY" + fi + + #Recap + file_log "" + file_log "" + file_log "" + file_log "" + + line_fill "$CHORIZONTAL" "$CLINESIZE" + log_ops_finish "User Name" "$CreateNonRootUser" "$NORM_USER_NAME" + log_ops_finish "User's Password" "$CreateNonRootUser" "$USER_PASS" + log_ops_finish "SSH Private Key File" "$CreateSSHKey" "$SSH_DIR"/"$NORM_USER_NAME".pem + log_ops_finish "SSH Public Key File" "$CreateSSHKey" "$SSH_DIR"/"$NORM_USER_NAME".pem.pub + log_ops_finish "SSH Key Passphrase" "$CreateSSHKey" "$KEY_PASS" + if [[ "$RESET_ROOT_PWD" == "y" ]]; then + log_ops_finish "New root Password" "$ChangeRootPwd" "$PASS_ROOT" + fi + line_fill "$CHORIZONTAL" "$CLINESIZE" + + log_ops_finish_file_contents "SSH Private Key" "$SSH_DIR"/"$NORM_USER_NAME".pem + log_ops_finish_file_contents "SSH Public Key" "$SSH_DIR"/"$NORM_USER_NAME".pem.pub + + line_fill "$CHORIZONTAL" "$CLINESIZE" + center_reg_text "!!! DO NOT LOG OUT JUST YET !!!" + center_reg_text "Use another window to test out the above credentials" + center_reg_text "If you face issue logging in look at the log file to see what went wrong" + center_reg_text "Log file at ${LOGFILE}" + + line_fill "$CHORIZONTAL" "$CLINESIZE" + echo + + if [[ $ChangeSourceList -eq 3 ]] || + [[ $InstallReqSoftwares -eq 3 ]] || + [[ $ConfigureUFW -eq 3 ]] || + [[ $ConfigureFail2Ban -eq 3 ]] + [[ $ScheduleUpdate -eq 3 ]] && + [[ $ChangeRootPwd -eq 3 ]]; then + center_err_text "Some operations failed..." + center_err_text "These may NOT be catastrophic" + center_err_text "Please check $LOGFILE file for details" + echo + fi + + if [[ $HIDE_CREDENTIALS == "y" ]]; then + center_reg_text "Issue the following command to see all credentials" + center_reg_text "tail -n 20 ${LOGFILE}" + fi +} + +function setup_step_start() { + reset_exit_code + update_step_status "$1" 1 + log_step_status "$1" +} + +function setup_step_end() { + # If it was a no op - Log and return + # Since we set step status to be 1 in setup_step_start(), + # it can become 0 only if we explicitly do the following INSIDE the step + # update_step_status "$1" 0 + step_status=$(get_step_status "$1") + if [[ $step_status -eq 0 ]]; then + log_step_status "$1" "NO-OP" + return + fi + + if [[ $exit_code -eq 0 ]]; then + update_step_status "$1" 2 + log_step_status "$1" "SUCCESSFUL" + else + reset_exit_code + update_step_status "$1" 3 + log_step_status "$1" "FAILED" + fi +} + ############################################################## # Step 1 - Create non-root user ############################################################## -function op_start() { - reset_op_code - update_event_status "$1" 1 - op_log "$1" -} - -function op_end() { - if [[ $1 -eq 0 ]]; then - update_event_status "$2" 2 - op_log "$2" "SUCCESSFUL" - else - reset_op_code - update_event_status "$2" 3 - op_log "$2" "FAILED" - fi -} - -op_start "${OP_TEXT[0]}" +setup_step_start "${STEP_TEXT[0]}" { if [[ $AUTO_GEN_USERNAME == 'y' ]]; then NORM_USER_NAME="$(< /dev/urandom tr -cd 'a-z' | head -c 6)""$(< /dev/urandom tr -cd '0-9' | head -c 2)" || exit 1 @@ -804,16 +820,16 @@ op_start "${OP_TEXT[0]}" # Create the user and assign the above password echo -e "${USER_PASS}\\n${USER_PASS}" | adduser "$NORM_USER_NAME" -q --gecos "First Last,RoomNumber,WorkPhone,HomePhone" - set_op_code $? + set_exit_code $? # Give root privilages to the above user usermod -aG sudo "$NORM_USER_NAME" - set_op_code $? + set_exit_code $? } 2>> "$LOGFILE" >&2 -op_end $OP_CODE "${OP_TEXT[0]}" -if [[ $OP_CODE -eq 3 ]]; then - revert_everything_and_exit "${OP_TEXT[0]}" +setup_step_end "${STEP_TEXT[0]}" +if [[ $exit_code -gt 0 ]]; then + revert_everything_and_exit "${STEP_TEXT[0]}" fi @@ -821,32 +837,32 @@ fi # Step 2 - Create SSH Key for the above new user ############################################################## -op_start "${OP_TEXT[1]}" +setup_step_start "${STEP_TEXT[1]}" { SSH_DIR=/home/"$NORM_USER_NAME"/.ssh file_log "Creating SSH directory - $SSH_DIR" mkdir "$SSH_DIR" - set_op_code $? + set_exit_code $? # Generate a 15 character random password for key file_log "Generating SSH Key Passphrase - ${KEY_PASS}" KEY_PASS="$(< /dev/urandom tr -cd "[:alnum:]" | head -c 15)" - set_op_code $? + set_exit_code $? # Create a OpenSSH-compliant ed25519-type key file_log "Generating SSH Key File - $SSH_DIR/$NORM_USER_NAME.pem" ssh-keygen -a 1000 -o -t ed25519 -N "$KEY_PASS" -C "$NORM_USER_NAME" -f "$SSH_DIR"/"$NORM_USER_NAME".pem -q - set_op_code $? + set_exit_code $? # Copy the generated public file to authorized_keys cat "$SSH_DIR"/"$NORM_USER_NAME".pem.pub >> "$SSH_DIR"/authorized_keys - set_op_code $? + set_exit_code $? } 2>> "$LOGFILE" >&2 -op_end $OP_CODE "${OP_TEXT[1]}" -if [[ $OP_CODE -eq 3 ]]; then +setup_step_end "${STEP_TEXT[1]}" +if [[ $exit_code -gt 0 ]]; then file_log "Creating SSH Key for new user failed." - revert_everything_and_exit "${OP_TEXT[1]}" + revert_everything_and_exit "${STEP_TEXT[1]}" fi @@ -854,7 +870,7 @@ fi # Step 3 - Secure authorized_keys file ############################################################## -op_start "${OP_TEXT[2]}" +setup_step_start "${STEP_TEXT[2]}" { # Set appropriate permissions for ".ssh" dir and "authorized_key" file file_log "Setting appropriate permissions for $SSH_DIR dir and $SSH_DIR/authorized_keys file" @@ -863,7 +879,7 @@ op_start "${OP_TEXT[2]}" chmod 700 "$SSH_DIR" && \ chmod 400 "$SSH_DIR"/authorized_keys && \ chattr +i "$SSH_DIR"/authorized_keys - set_op_code $? + set_exit_code $? # Restrict access to the generated SSH Key files as well shopt -s nullglob @@ -872,15 +888,15 @@ op_start "${OP_TEXT[2]}" file_log "Restricting access (chmod 400 and chattr +i) to ${key} file" chmod 400 "$key" && \ chattr +i "$key" - set_op_code $? + set_exit_code $? done } 2>> "$LOGFILE" >&2 -op_end $OP_CODE "${OP_TEXT[2]}" -if [[ $OP_CODE -eq 3 ]]; then +setup_step_end "${STEP_TEXT[2]}" +if [[ $exit_code -gt 0 ]]; then file_log "Setting restrictive permissions for '~/.ssh/' directory failed" file_log "Please do 'ls -lAh ~/.ssh/' and check manually to see what went wrong." - revert_everything_and_exit "${OP_TEXT[2]}" + revert_everything_and_exit "${STEP_TEXT[2]}" fi @@ -890,15 +906,15 @@ fi if [[ $DEFAULT_SOURCE_LIST = "y" ]]; then # Low priority - But what to do if it fails??? - op_start "${OP_TEXT[4]}" + setup_step_start "${STEP_TEXT[4]}" { file_log "Backing up /etc/apt/sources.list file to /etc/apt/sources.list${BACKUP_EXTENSION}" cp /etc/apt/sources.list /etc/apt/sources.list"${BACKUP_EXTENSION}" - set_op_code $? + set_exit_code $? file_log "Commenting out everthing in /etc/apt/sources.list" sed -i "1,$(wc -l < /etc/apt/sources.list) s/^/#/" /etc/apt/sources.list - set_op_code $? + set_exit_code $? if [[ $OS = "debian" ]]; then @@ -920,7 +936,7 @@ deb-src http://deb.debian.org/debian ${DEB_VER_STR}-updates main contrib non-fre deb http://deb.debian.org/debian ${DEB_VER_STR}-backports main contrib non-free deb-src http://deb.debian.org/debian ${DEB_VER_STR}-backports main contrib non-free DEBIAN - set_op_code $? + set_exit_code $? elif [[ $OS = "ubuntu" ]]; then @@ -946,7 +962,7 @@ deb-src http://security.ubuntu.com/ubuntu ${UBT_VER_STR}-security universe deb http://security.ubuntu.com/ubuntu ${UBT_VER_STR}-security multiverse deb-src http://security.ubuntu.com/ubuntu ${UBT_VER_STR}-security multiverse UBUNTU - set_op_code $? + set_exit_code $? fi # Find any additional sources listed by the provider and comment them out @@ -956,17 +972,17 @@ UBUNTU do file_log "Backing up ${file} file to ${file}${BACKUP_EXTENSION}" cp "$file" "$file""${BACKUP_EXTENSION}" - set_op_code $? + set_exit_code $? file_log "Commenting out the ${file}" sed -i "1,$(wc -l < "$file") s/^/#/" "$file" - set_op_code $? + set_exit_code $? done fi } 2>> "$LOGFILE" >&2 - op_end $OP_CODE "${OP_TEXT[4]}" - if [[ $OP_CODE -eq 3 ]]; then + setup_step_end "${STEP_TEXT[4]}" + if [[ $exit_code -gt 0 ]]; then revert_source_list_changes fi fi @@ -976,16 +992,16 @@ fi # Step 5 - Install required softwares ############################################################## -op_start "${OP_TEXT[5]}" +setup_step_start "${STEP_TEXT[5]}" { apt-get update export DEBIAN_FRONTEND=noninteractive ; apt-get upgrade -y -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" apt-get install -y sudo curl screen ufw fail2ban - set_op_code $? + set_exit_code $? } 2>> "$LOGFILE" >&2 -op_end $OP_CODE "${OP_TEXT[5]}" -if [[ $OP_CODE -eq 3 ]]; then +setup_step_end "${STEP_TEXT[5]}" +if [[ $exit_code -gt 0 ]]; then revert_software_installs fi @@ -998,24 +1014,24 @@ ufw status 2>> /dev/null >&2 # Proceed only when UFW is installed if [[ $? -eq 0 ]]; then - op_start "${OP_TEXT[6]}" + setup_step_start "${STEP_TEXT[6]}" { file_log "Setting ufw for ssh, http, https" ufw allow ssh && ufw allow http && ufw allow https - set_op_code $? + set_exit_code $? file_log "Enabling ufw" echo "y" | ufw enable - set_op_code $? + set_exit_code $? } 2>> "$LOGFILE" >&2 - - op_end $OP_CODE "${OP_TEXT[6]}" - if [[ $OP_CODE -eq 3 ]]; then - revert_config_UFW - fi else - op_log "${OP_TEXT[6]}" "NO-OP" - file_log "Skipping UFW Config since software install failed..." + update_step_status "$1" 0 + file_log "Skipping UFW config as it does not seem to be installed - check log to know more" +fi + +setup_step_end "${STEP_TEXT[6]}" +if [[ $exit_code -gt 0 ]]; then + revert_config_UFW fi @@ -1025,20 +1041,20 @@ fi # Proceed only when Fail2ban is installed if [[ $(dpkg -l | grep -c fail2ban) -gt 0 ]]; then - op_start "${OP_TEXT[7]}" + setup_step_start "${STEP_TEXT[7]}" { if [[ -f /etc/fail2ban/jail.local ]]; then file_log "Backing up /etc/fail2ban/jail.local to /etc/fail2ban/jail.local${BACKUP_EXTENSION}" cp /etc/fail2ban/jail.local /etc/fail2ban/jail.local"$BACKUP_EXTENSION" - set_op_code $? + set_exit_code $? else file_log "Copying /etc/fail2ban/jail.conf to /etc/fail2ban/jail.local" cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local - set_op_code $? + set_exit_code $? file_log "Backing up /etc/fail2ban/jail.conf to /etc/fail2ban/jail.conf${BACKUP_EXTENSION}" cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.conf"$BACKUP_EXTENSION" - set_op_code $? + set_exit_code $? fi # Do not do anything if copying jail.conf to jail.local failed @@ -1049,15 +1065,15 @@ if [[ $(dpkg -l | grep -c fail2ban) -gt 0 ]]; then # Start search from the line that contains "[DEFAULT]" - end search before the line that contains "# JAILS" file_log "/etc/fail2ban/jail.local - Setting bantime = 18000" sed -ri "/^\[DEFAULT\]$/,/^# JAILS$/ s/^bantime[[:blank:]]*= .*/bantime = 18000/" /etc/fail2ban/jail.local - set_op_code $? + set_exit_code $? file_log "/etc/fail2ban/jail.local - Setting backend = polling" sed -ri "/^\[DEFAULT\]$/,/^# JAILS$/ s/^backend[[:blank:]]*=.*/backend = polling/" /etc/fail2ban/jail.local - set_op_code $? + set_exit_code $? file_log "/etc/fail2ban/jail.local - Setting ignoreip = 127.0.0.1/8 ::1 ${pub_ip}" sed -ri "/^\[DEFAULT\]$/,/^# JAILS$/ s/^ignoreip[[:blank:]]*=.*/ignoreip = 127.0.0.1\/8 ::1 ${pub_ip}/" /etc/fail2ban/jail.local - set_op_code $? + set_exit_code $? # TODO - Exception handle # - No [DEFAULT] section present @@ -1068,7 +1084,7 @@ if [[ $(dpkg -l | grep -c fail2ban) -gt 0 ]]; then if [[ -f /etc/fail2ban/jail.d/defaults-debian.conf ]]; then file_log "Backing up /etc/fail2ban/jail.d/defaults-debian.conf to /etc/fail2ban/jail.d/defaults-debian.conf${BACKUP_EXTENSION}" cp /etc/fail2ban/jail.d/defaults-debian.conf /etc/fail2ban/jail.d/defaults-debian.conf"$BACKUP_EXTENSION" - set_op_code $? + set_exit_code $? fi file_log "Enabling jails in /etc/fail2ban/jail.d/defaults-debian.conf" @@ -1089,18 +1105,18 @@ bantime = 31536000 ; 1 year findtime = 86400 ; 1 days maxretry = 10 FAIL2BAN - set_op_code $? + set_exit_code $? - set_op_code $(service_action_and_chk_error "fail2ban" "start") + set_exit_code $(service_action_and_chk_error "fail2ban" "start") } 2>> "$LOGFILE" >&2 - - op_end $OP_CODE "${OP_TEXT[7]}" - if [[ $OP_CODE -eq 3 ]]; then - revert_config_fail2ban - fi else - op_log "${OP_TEXT[7]}" "NO-OP" - file_log "Skipping Fail2Ban config since software installation failed..." + update_step_status "$1" 0 + file_log "Skipping Fail2Ban config as it does not seem to be installed - check log to know more" +fi + +setup_step_end "${STEP_TEXT[7]}" +if [[ $exit_code -gt 0 ]]; then + revert_config_fail2ban fi @@ -1108,28 +1124,28 @@ fi # Step 8 - Schedule cron for daily system update ############################################################## -op_start "${OP_TEXT[9]}" +setup_step_start "${STEP_TEXT[9]}" { dailycron_filename=/etc/cron.daily/linux_init_harden_apt_update.sh # Check if we created a schedule already if [[ -f $dailycron_filename ]] ; then - true + update_step_status "$1" 0 else # If not created already - create one into the file file_log "Adding our schedule to the script file ${dailycron_filename}" echo "#!/bin/sh" >> $dailycron_filename echo 'apt-get update && apt-get -y -d upgrade' >> $dailycron_filename - set_op_code $? + set_exit_code $? file_log "Granting execute permission on ${dailycron_filename} file" chmod +x $dailycron_filename - set_op_code $? + set_exit_code $? fi } 2>> "$LOGFILE" >&2 -op_end $OP_CODE "${OP_TEXT[9]}" -if [[ $OP_CODE -eq 3 ]]; then +setup_step_end "${STEP_TEXT[9]}" +if [[ $exit_code -gt 0 ]]; then revert_schedule_updates fi @@ -1139,23 +1155,23 @@ fi ############################################################## if [[ $RESET_ROOT_PWD == 'y' ]]; then - op_start "${OP_TEXT[8]}" + setup_step_start "${STEP_TEXT[8]}" { # Generate a 15 character random password file_log "Generating roots new password..." PASS_ROOT="$(< /dev/urandom tr -cd "[:alnum:]" | head -c 15)" - set_op_code $? + set_exit_code $? file_log "Generated root Password - ${PASS_ROOT}" # Change root's password file_log "Setting the new root password" echo -e "${PASS_ROOT}\\n${PASS_ROOT}" | passwd - set_op_code $? + set_exit_code $? } 2>> "$LOGFILE" >&2 - op_end $OP_CODE "${OP_TEXT[8]}" - if [[ $OP_CODE -eq 3 ]]; then + setup_step_end "${STEP_TEXT[8]}" + if [[ $exit_code -gt 0 ]]; then revert_root_pass_change fi fi @@ -1242,44 +1258,44 @@ function set_config_key(){ fi } -op_start "${OP_TEXT[3]}" +setup_step_start "${STEP_TEXT[3]}" { # Backup the sshd_config file file_log "Backing up /etc/ssh/sshd_config file to /etc/ssh/sshd_config$BACKUP_EXTENSION" cp /etc/ssh/sshd_config /etc/ssh/sshd_config"$BACKUP_EXTENSION" - set_op_code $? + set_exit_code $? # Remove root login file_log "Removing root login -> PermitRootLogin no" set_config_key "/etc/ssh/sshd_config" "PermitRootLogin" "no" - set_op_code $? + set_exit_code $? # Disable password login file_log "Disabling password login -> PasswordAuthentication no" set_config_key "/etc/ssh/sshd_config" "PasswordAuthentication" "no" - set_op_code $? + set_exit_code $? # Set SSH Authorization-Keys path file_log "Setting SSH Authorization-Keys path -> AuthorizedKeysFile '%h\/\.ssh\/authorized_keys'" set_config_key "/etc/ssh/sshd_config" "AuthorizedKeysFile" '\.ssh\/authorized_keys %h\/\.ssh\/authorized_keys' - set_op_code $? + set_exit_code $? file_log "Restarting ssh service..." { - set_op_code $(service_action_and_chk_error "sshd" "restart") - if [[ $OP_CODE -eq 0 ]]; then + set_exit_code $(service_action_and_chk_error "sshd" "restart") + if [[ $exit_code -eq 0 ]]; then false fi } || { # Because Ubuntu 14.04 does not have sshd - set_op_code $(service_action_and_chk_error "ssh" "restart") + set_exit_code $(service_action_and_chk_error "ssh" "restart") } } 2>> "$LOGFILE" >&2 -op_end $OP_CODE "${OP_TEXT[3]}" -if [[ $OP_CODE -eq 3 ]]; then +setup_step_end "${STEP_TEXT[3]}" +if [[ $exit_code -gt 0 ]]; then file_log "Enabling SSH-only login failed." - revert_everything_and_exit "${OP_TEXT[3]}" + revert_everything_and_exit "${STEP_TEXT[3]}" fi @@ -1287,4 +1303,4 @@ fi # Recap ############################################################## -finally \ No newline at end of file +recap \ No newline at end of file