This commit is contained in:
Pratik
2019-01-18 09:45:16 +05:30
parent c533bb8937
commit c4de5a088b

View File

@@ -28,7 +28,10 @@
SCRIPT_NAME=server_harden SCRIPT_NAME=server_harden
SCRIPT_VERSION=0.2 SCRIPT_VERSION=0.2
LOGFILE=/tmp/"$SCRIPT_NAME"_v"$SCRIPT_VERSION".log LOGFILE=/tmp/"$SCRIPT_NAME"_v"$SCRIPT_VERSION".log
BACKUP_EXTENSION='.'$(date '+%d%m%Y%H%M%S')"_bak" # Reset previous log file
TS=$(date '+%d_%m_%Y-%H_%M_%S')
echo "Starting $0 - $TS" > "$LOGFILE"
BACKUP_EXTENSION='.'$TS"_bak"
# Colors # Colors
CSI='\033[' CSI='\033['
@@ -36,13 +39,40 @@ CEND="${CSI}0m"
CRED="${CSI}1;31m" CRED="${CSI}1;31m"
CGREEN="${CSI}1;32m" CGREEN="${CSI}1;32m"
##############################################################
# Usage
##############################################################
# Script takes arguments as follows
# init-linux-harden -username=pratik --resetrootpwd
# init-linux-harden -u pratik --resetrootpwd
function usage() {
if [ -n "$1" ]; then
echo ""
echo -e "${CRED}$1${CEND}\n"
fi
echo "Usage: sudo bash $0 [-u|--username username] [-r|--resetrootpwd] [--defaultsourcelist]"
echo " -u, --username Username for your server (If omitted script will choose an username for you)"
echo " -r, --resetrootpwd Reset current root password"
echo " -d, --defaultsourcelist Updates /etc/apt/sources.list to download software from debian.org."
echo " NOTE - If you fail to update system after using it, you need to manually reset it. This script keeps a backup in the same folder."
echo ""
echo "Example: $0 --username myuseraccount --resetrootpwd"
printf "\\nBelow restrictions apply to username this script accepts - \\n"
printf "%2s - [a-zA-Z0-9] [-] [_] are allowed\\n%2s - NO special characters.\\n%2s - NO spaces.\\n" " " " " " "
}
############################################################## ##############################################################
# Basic checks before starting # Basic checks before starting
############################################################## ##############################################################
# No root - no good # No root - no good
[ "$(id -u)" != "0" ] && { [ "$(id -u)" != "0" ] && {
printf "ERROR: You must be root to run this script.\\nPlease login as root and execute the script again." usage "ERROR: You must be root to run this script.\\nPlease login as root and execute the script again."
exit 1 exit 1
} }
@@ -57,32 +87,11 @@ else
exit 1 exit 1
fi fi
################################## ##################################
# Parse script arguments # Parse script arguments
################################## ##################################
# Script takes arguments as follows
# init-linux-harden -username=pratik --resetroot
# init-linux-harden -u pratik --resetroot
function usage() {
if [ -n "$1" ]; then
echo ""
echo -e "${CRED}$1${CEND}\n"
fi
echo "Usage: $0 [-u|--username username] [-r|--resetrootpwd] [--defaultsourcelist]"
echo " -u, --username Username for your server (If omitted script will choose an username for you)"
echo " -r, --resetrootpwd Reset current root password"
echo " -d, --defaultsourcelist Updates /etc/apt/sources.list to download software from debian.org."
echo " NOTE - If you fail to update system after using it, you need to manually reset it. This script keeps a backup in the same folder."
echo ""
echo "Example: $0 --username myuseraccount --resetrootpwd"
printf "\\nBelow restrictions apply to username this script accepts - \\n"
printf "%2s - [a-zA-Z0-9] [-] [_] are allowed\\n%2s - NO special characters.\\n%2s - NO spaces.\\n" " " " " " "
}
# defaults # defaults
AUTO_GEN_USERNAME="y" AUTO_GEN_USERNAME="y"
RESET_ROOT_PWD="n" RESET_ROOT_PWD="n"
@@ -130,39 +139,112 @@ done
############################################################## ##############################################################
# Log # Display what the script does
############################################################## ##############################################################
CVERTICAL="|" clear
CHORIZONTAL="_" cat <<INFORM1 | more
!!! READ BELOW & PRESS ENTER/RETURN TO CONTINUE !!!
##################################################################
INFORM1
echo "Installation options selected - " | tee -a "$LOGFILE"
if [[ "$AUTO_GEN_USERNAME" == "y" ]]; then
printf "%3s Non-root Username will be auto generated by script\\n" " -" | tee -a "$LOGFILE"
else
printf "%3s Non-root Username = %s\\n" " -" "$NORM_USER_NAME" | tee -a "$LOGFILE"
fi
if [[ "$DEFAULT_SOURCE_LIST" == "y" ]]; then
printf "%3s Use debian.org in /etc/apt/source.list file\\n" " -" | tee -a "$LOGFILE"
fi
if [[ "$RESET_ROOT_PWD" == "y" ]]; then
printf "%3s Reset root password\\n" " -" | tee -a "$LOGFILE"
fi
cat <<INFORM2 | more
All backup files have extension (${BACKUP_EXTENSION})
Script logs all operation into (${LOGFILE}) file.
- Before editing any file, script creates a back up of that file in
the same directory. If script detects any error, then it restores the
original files.
- Script assumes you are running this on a brand new VPS and that
DATALOSS OR LOSS OF ACCESS TO THE SERVER IS NOT A MAJOR CONCERN. If
you do however lose access to the server most VPS provider allow to
create a new one easily.
- If any operation which involves credentials generation, succeeds -
then those credentials will be displayed at the end of all operations.
- If script reports any error or something does not work as expected,
please take a look at the log file at (${LOGFILE}).
TO CONTINUE (press enter/return)...
TO EXIT (ctrl + c)...
INFORM2
read -r
function horizontal_fill() { ##############################################################
local char=$1 # Error Handling
declare -i rep=$2 ##############################################################
for ((x = 0; x < "$rep"; x++)); do
printf %s "$char" # keep state of the script
done # Each step has 3 states
# 0 - not executed
# 1 - Started
# 2 - Completed
# 3 - Failed
CreateNonRootUser=0
CreateSSHKey=0
AddToAuthkeysfile=0
SecureAuthkeysfile=0
EnableSSHOnly=0
ChangeSourceList=0
InstallReqSoftwares=0
ChangeRootPwd=0
function get_event_var_from_event() {
case $1 in
"Creating new user")
echo "CreateNonRootUser"
;;
"Creating SSH Key for new user")
echo "CreateSSHKey"
;;
"Adding SSH Key to 'authorized_keys' file")
echo "AddToAuthkeysfile"
;;
"Securing 'authorized_keys' file")
echo "SecureAuthkeysfile"
;;
"Enabling SSH-only login")
echo "EnableSSHOnly"
;;
"Changing urls in sources.list to defaults")
echo "ChangeSourceList"
;;
"Installing required softwares")
echo "InstallReqSoftwares"
;;
"Changing root password")
echo "ChangeRootPwd"
;;
*)
false
;;
esac
} }
function line_fill() { function update_event_status() {
horizontal_fill "$1" "$2" local event
printf "\\n" event=$(get_event_var_from_event "$1")
eval "$event"="$2"
} }
function recap (){ function get_event_status() {
local purpose=$1 local event=get_event_var_from_event "$1"
local value=$2 return ${!event}
if [[ $value ]]; then
value="[${CGREEN}${value}${CEND}]"
else
value="${CRED}-FAILED-${CEND}"
fi
horizontal_fill "$CVERTICAL" 1
printf "%20s:%5s%-33s" "$purpose" " " "$(echo -e "$value")"
line_fill "$CVERTICAL" 1
} }
function revert_changes(){ function revert_changes(){
@@ -190,9 +272,9 @@ function revert_changes(){
} }
function revert_create_user(){ function revert_create_user(){
# Remove user and # Remove user and its home directory only if user was created
if [[ $(getent passwd "$NORM_USER_NAME" | wc -l) -gt 0 ]]; then if [[ $(getent passwd "$NORM_USER_NAME" | wc -l) -gt 0 ]]; then
deluser --remove-home "$NORM_USER_NAME" deluser --remove-home "$NORM_USER_NAME" 2>> "$LOGFILE" >&2
fi fi
} }
@@ -257,29 +339,25 @@ function revert_root_pass_change(){
true true
} }
function finally(){
# Check if $what_failed is one of the catastrophic failures
# if - Catastrofic failure - Check if any .bkp file exist and revert them to original
# Let user know nothing was changed
# if - Non-catastrophic failure - Inform user of side effects
#local what_failed=$1
line_fill "$CHORIZONTAL" 60 ##############################################################
recap "New root Password" "$PASS_ROOT" # Log
recap "User Name" "$NORM_USER_NAME" ##############################################################
recap "User's Password" "$USER_PASS"
recap "User's SSH Private Key Location" "$KEY_PASS"
recap "User's SSH Public Key Location" "$KEY_PASS"
recap "User's SSH Key Passphrase" "$KEY_PASS"
line_fill "$CHORIZONTAL" 60
# If something failed - try to revert things back CVERTICAL="|"
if [[ "$#" -gt 0 ]]; then CHORIZONTAL="_"
# show - something failed - trying to restore required changes
revert_changes "$1"
# If restoration failed - well you are f**ked function horizontal_fill() {
fi local char=$1
declare -i rep=$2
for ((x = 0; x < "$rep"; x++)); do
printf %s "$char"
done
}
function line_fill() {
horizontal_fill "$1" "$2"
printf "\\n"
} }
function file_log(){ function file_log(){
@@ -292,69 +370,63 @@ function op_log() {
if [ "$RESULT" = "SUCCESSFUL" ] if [ "$RESULT" = "SUCCESSFUL" ]
then then
printf "\r%30s %7s [${CGREEN}${RESULT}${CEND}]\\n" "$EVENT" " " update_event_status "${EVENT}" 2
printf "\r%33s %7s [${CGREEN}${RESULT}${CEND}]\\n" "$EVENT" " "
file_log "${EVENT} - ${RESULT}" file_log "${EVENT} - ${RESULT}"
elif [ "$RESULT" = "FAILED" ] elif [ "$RESULT" = "FAILED" ]
then then
printf "\r%30s %7s [${CRED}${RESULT}${CEND}]\\n" "$EVENT" " " update_event_status "${EVENT}" 3
printf "\r%33s %7s [${CRED}${RESULT}${CEND}]\\n" "$EVENT" " "
printf "\\n\\nPlease look at %s\\n\\n" "$LOGFILE" printf "\\n\\nPlease look at %s\\n\\n" "$LOGFILE"
file_log "${EVENT} - ${RESULT}" file_log "${EVENT} - ${RESULT}"
finally "$EVENT" finally "$EVENT"
else else
printf "%30s %7s [${CRED}..${CEND}]" "$EVENT" " " update_event_status "${EVENT}" 1
printf "%33s %7s [${CRED}..${CEND}]" "$EVENT" " "
file_log "${EVENT} - begin..." file_log "${EVENT} - begin..."
fi fi
} }
# Reset previous log file function recap (){
echo "Starting $0 - $(date '+%d-%b-%Y %H:%M:%S')" > "$LOGFILE" local purpose=$1
local status=$2
local value=$3
if [[ $status -eq 0 ]]; then
value="[${CGREEN}--NO_OP--${CEND}]"
elif [[ $status -eq 2 ]]; then
value="[${CGREEN}${value}${CEND}]"
elif [[ $status -eq 1 ]] || [[ $status -eq 3 ]]; then
value="${CRED}--ERROR--${CEND}"
fi
############################################################## horizontal_fill "$CVERTICAL" 1
# Display what the script does printf "%20s:%3s%-49s" "$purpose" " " "$(echo -e "$value")"
############################################################## line_fill "$CVERTICAL" 1
}
clear function finally(){
cat <<INFORM | more line_fill "$CHORIZONTAL" 64
!!! READ BELOW & PRESS ENTER TO CONTINUE !!! recap "User Name" "$CreateNonRootUser" "$NORM_USER_NAME"
################################################################## recap "User's Password" "$CreateNonRootUser" "$USER_PASS"
This script performs the following tasks :- recap "SSH Private Key File" "$CreateSSHKey" "$SSH_DIR"/"$NORM_USER_NAME".pem
1 - Change your root password (unless you have choosen NOT to) recap "SSH Public Key File" "$CreateSSHKey" "$SSH_DIR"/"$NORM_USER_NAME".pem.pub
2 - Create a non-root user (unless provided by you a random recap "SSH Key Passphrase" "$CreateSSHKey" "$KEY_PASS"
username will be created) if [[ "$RESET_ROOT_PWD" == "y" ]]; then
3 - Generate SSH keys on the server and store them at '~/.ssh' recap "New root Password" "$ChangeRootPwd" "$PASS_ROOT"
4 - Adds the public key from the above step to fi
'~/.ssh/authorized_keys' file line_fill "$CHORIZONTAL" 64
5 - Restricts access to '~/.ssh' folder echo
6 - Restricts login method to SSH-only by editing echo
'/etc/ssh/sshd_config' file to enable
7 - Restores the '/etc/apt/sources.list'
(Most server provider alter these to serve software from
their CDNs)
8 - Installs "sudo" "curl" "screen"
9 - Display the following at the end
a) root password (if changed)
b) user name
c) user password
d) SSH Private Key
e) SSH Public Key
Before editing any file, script creates a back up of that file with # If something failed - try to revert things back
in the same directory. If script detects any catastrophic error, then if [[ "$#" -gt 0 ]]; then
it restores the original files. Script assumes you are running this # show - something failed - trying to restore required changes
on a brand new VPS and that DATALOSS OR LOSS OF ACCESS TO THE SERVER revert_changes "$1"
IS NOT A MAJOR CONCERN. If you do however lose access to the server
most VPS provider allow to create a new one easily.
All backup files have extension (${BACKUP_EXTENSION}) # If restoration failed - well you are f**ked
A log file can be found at ${LOGFILE} fi
}
TO CONTINUE (press enter/return)...
TO EXIT (ctrl + c)...
INFORM
read -r
clear
############################################################## ##############################################################
@@ -364,26 +436,25 @@ clear
op_log "Creating new user" op_log "Creating new user"
{ {
if [[ $AUTO_GEN_USERNAME == 'y' ]]; then 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 NORM_USER_NAME="$(< /dev/urandom tr -cd 'a-z' | head -c 6)""$(< /dev/urandom tr -cd '0-9' | head -c 2)" || false
file_log "Generated user name - ${NORM_USER_NAME}" file_log "Generated user name - ${NORM_USER_NAME}"
fi fi
# Generate a 15 character random password # Generate a 15 character random password
USER_PASS="$(< /dev/urandom tr -cd "[:alnum:]" | head -c 15)" || exit 1 USER_PASS="$(< /dev/urandom tr -cd "[:alnum:]" | head -c 15)"
file_log "Generated user password - ${USER_PASS}" file_log "Generated user password - ${USER_PASS}"
# Create the user and assign the above password # 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" echo -e "${USER_PASS}\\n${USER_PASS}" | adduser "$NORM_USER_NAME" -q --gecos "First Last,RoomNumber,WorkPhone,HomePhone" || false
# Give root privilages to the above user # Give root privilages to the above user
usermod -aG sudo "$NORM_USER_NAME" || exit 1 usermod -aG sudo "$NORM_USER_NAME"
} 2>> "$LOGFILE" >&2 } 2>> "$LOGFILE" >&2
if [[ $? -eq 0 ]]; then if [[ $? -eq 0 ]]; then
op_log "Creating new user" "SUCCESSFUL" op_log "Creating new user" "SUCCESSFUL"
else else
op_log "Creating new user" "FAILED" op_log "Creating new user" "FAILED"
finally "CNU"
exit 1; exit 1;
fi fi
@@ -401,19 +472,22 @@ op_log "Creating SSH Key for new user"
# If SSH files already exist - rename them to .timestamp_bkp # If SSH files already exist - rename them to .timestamp_bkp
if [[ ${#KEY_FILES[@]} -gt 0 ]]; then if [[ ${#KEY_FILES[@]} -gt 0 ]]; then
for key in "${KEY_FILES[@]}"; do for key in "${KEY_FILES[@]}"; do
cp "$key" "$key""$BACKUP_EXTENSION" || exit 1 cp "$key" "$key""$BACKUP_EXTENSION" || false
file_log "SSH Key File exists ($key) - Making backup ($key$BACKUP_EXTENSION) of the existing file"
done done
fi fi
SSH_DIR=/home/"$NORM_USER_NAME"/.ssh SSH_DIR=/home/"$NORM_USER_NAME"/.ssh
mkdir "$SSH_DIR" || exit 1 mkdir "$SSH_DIR" || false
file_log "Created SSH directory - $SSH_DIR"
# Generate a 15 character random password for key # Generate a 15 character random password for key
KEY_PASS="$(< /dev/urandom tr -cd "[:alnum:]" | head -c 15)" || exit 1 KEY_PASS="$(< /dev/urandom tr -cd "[:alnum:]" | head -c 15)"
file_log "Generated SSH Key Passphrase - ${KEY_PASS}" file_log "Generated SSH Key Passphrase - ${KEY_PASS}"
# Create a OpenSSH-compliant ed25519-type key # Create a OpenSSH-compliant ed25519-type key
ssh-keygen -a 1000 -o -t ed25519 -N "$KEY_PASS" -C "$NORM_USER_NAME" -f "$SSH_DIR"/"$NORM_USER_NAME".pem -q || exit 1 ssh-keygen -a 1000 -o -t ed25519 -N "$KEY_PASS" -C "$NORM_USER_NAME" -f "$SSH_DIR"/"$NORM_USER_NAME".pem -q || false
file_log "Generated SSH Key File - $SSH_DIR/$NORM_USER_NAME.pem"
# TODO - Below would capture bak files as well - filter out the bak files # TODO - Below would capture bak files as well - filter out the bak files
# See if the files actually got created # See if the files actually got created
@@ -421,7 +495,7 @@ op_log "Creating SSH Key for new user"
if [[ ${#KEY_FILES[@]} -eq 0 ]]; then if [[ ${#KEY_FILES[@]} -eq 0 ]]; then
file_log "Unknown error occured." file_log "Unknown error occured."
file_log "Could not create SSH key files." file_log "Could not create SSH key files."
exit 1 retun false
fi fi
} 2>> "$LOGFILE" >&2 } 2>> "$LOGFILE" >&2
@@ -429,35 +503,6 @@ if [[ $? -eq 0 ]]; then
op_log "Creating SSH Key for new user" "SUCCESSFUL" op_log "Creating SSH Key for new user" "SUCCESSFUL"
else else
op_log "Creating SSH Key for new user" "FAILED" op_log "Creating SSH Key for new user" "FAILED"
finally "CSK"
exit 1;
fi
##############################################################
# Add generated key to authorized_keys file
##############################################################
op_log "Adding SSH Key to 'authorized_keys' file"
{
# If 'authorized_keys' exists create backup
# BACKUP_EXTENSION
if [[ -e "$SSH_DIR"/authorized_keys ]]; then
cp "$SSH_DIR"/authorized_keys "$SSH_DIR"/authorized_keys"$BACKUP_EXTENSION" || exit 1
else
# Create authorized_keys if it does not exist yet
touch "$SSH_DIR"/authorized_keys || exit 1
fi
# Insert the public key into "authoried_keys" file
cat "$SSH_DIR"/"$NORM_USER_NAME".pem.pub >> "$SSH_DIR"/authorized_keys || exit 1
} 2>> "$LOGFILE" >&2
if [[ $? -eq 0 ]]; then
op_log "Adding SSH Key to 'authorized_keys' file" "SUCCESSFUL"
else
op_log "Adding SSH Key to 'authorized_keys' file" "FAILED"
finally "ATAF"
exit 1; exit 1;
fi fi
@@ -473,13 +518,16 @@ op_log "Securing 'authorized_keys' file"
chgrp -R "$NORM_USER_NAME" "$SSH_DIR" && \ chgrp -R "$NORM_USER_NAME" "$SSH_DIR" && \
chmod 700 "$SSH_DIR" && \ chmod 700 "$SSH_DIR" && \
chmod 400 "$SSH_DIR"/authorized_keys && \ chmod 400 "$SSH_DIR"/authorized_keys && \
chattr +i "$SSH_DIR"/authorized_keys chattr +i "$SSH_DIR"/authorized_keys || false
file_log "Set appropriate permissions for $SSH_DIR dir and $SSH_DIR/authorized_keys file"
# Restrict access to the generated SSH Key files as well # Restrict access to the generated SSH Key files as well
for key in "${KEY_FILES[@]}"; do for key in "${KEY_FILES[@]}"; do
chmod 400 "$key" && \ chmod 400 "$key" && \
chattr +i "$key" chattr +i "$key"
done done
file_log "Restrict access to the generated SSH Key files"
} 2>> "$LOGFILE" >&2 } 2>> "$LOGFILE" >&2
if [[ $? -eq 0 ]]; then if [[ $? -eq 0 ]]; then
@@ -575,16 +623,20 @@ function set_config_key(){
op_log "Enabling SSH-only login" op_log "Enabling SSH-only login"
{ {
# Backup the sshd_config file # Backup the sshd_config file
cp /etc/ssh/sshd_config /etc/ssh/sshd_config"$BACKUP_EXTENSION" || exit 1 cp /etc/ssh/sshd_config /etc/ssh/sshd_config"$BACKUP_EXTENSION" || false
file_log "Backed up /etc/ssh/sshd_config file to /etc/ssh/sshd_config$BACKUP_EXTENSION"
# Remove root login # Remove root login
set_config_key "/etc/ssh/sshd_config" "PermitRootLogin" "no" set_config_key "/etc/ssh/sshd_config" "PermitRootLogin" "no" || false
file_log "Remove root login -> PermitRootLogin no"
# Disable password login # Disable password login
set_config_key "/etc/ssh/sshd_config" "PasswordAuthentication" "no" set_config_key "/etc/ssh/sshd_config" "PasswordAuthentication" "no" || false
file_log "Disable password login -> PasswordAuthentication no"
# Set SSH Authorization-Keys path # Set SSH Authorization-Keys path
set_config_key "/etc/ssh/sshd_config" "AuthorizedKeysFile" '%h\/\.ssh\/authorized_keys' set_config_key "/etc/ssh/sshd_config" "AuthorizedKeysFile" '%h\/\.ssh\/authorized_keys' || false
file_log "Set SSH Authorization-Keys path -> AuthorizedKeysFile '%h\/\.ssh\/authorized_keys'"
systemctl restart sshd systemctl restart sshd
} 2>> "$LOGFILE" >&2 } 2>> "$LOGFILE" >&2
@@ -593,7 +645,6 @@ if [[ $? -eq 0 ]]; then
op_log "Enabling SSH-only login" "SUCCESSFUL" op_log "Enabling SSH-only login" "SUCCESSFUL"
else else
op_log "Enabling SSH-only login" "FAILED" op_log "Enabling SSH-only login" "FAILED"
finally "ESOL"
exit 1; exit 1;
fi fi
@@ -654,7 +705,6 @@ TAG
fi fi
############################################################## ##############################################################
# Install required softwares # Install required softwares
############################################################## ##############################################################
@@ -676,10 +726,10 @@ fi
############################################################## ##############################################################
if [[ $RESET_ROOT_PWD == 'y' ]]; then if [[ $RESET_ROOT_PWD == 'y' ]]; then
op_log " " op_log "Changing root password"
{ {
# Generate a 15 character random password # Generate a 15 character random password
PASS_ROOT="$(< /dev/urandom tr -cd "[:alnum:]" | head -c 15)" || exit 1 PASS_ROOT="$(< /dev/urandom tr -cd "[:alnum:]" | head -c 15)" || false
file_log "Generated Root Password - ${PASS_ROOT}" file_log "Generated Root Password - ${PASS_ROOT}"