This commit is contained in:
Pratik
2019-01-16 21:51:40 +05:30
parent 07b240a9f7
commit c533bb8937

View File

@@ -14,7 +14,7 @@
# Ask him to report back if he can login using the new user -with the ssh-private key
# - tell him to talk to the server provider's support to get help regarding SSH-only access
# What to do if making .bkp file fails?
# Add timestamp to all backup files filename.071218_171731_bak
# Add timestamp` to all backup files filename.071218_171731_bak
#Test
# 1 - Deb 9.x
# 2 - Deb 8.x
@@ -28,6 +28,13 @@
SCRIPT_NAME=server_harden
SCRIPT_VERSION=0.2
LOGFILE=/tmp/"$SCRIPT_NAME"_v"$SCRIPT_VERSION".log
BACKUP_EXTENSION='.'$(date '+%d%m%Y%H%M%S')"_bak"
# Colors
CSI='\033['
CEND="${CSI}0m"
CRED="${CSI}1;31m"
CGREEN="${CSI}1;32m"
##############################################################
# Basic checks before starting
@@ -50,58 +57,85 @@ else
exit 1
fi
##################################
# Parse script arguments
##################################
##############################################################
# Gather info
##############################################################
# Script takes arguments as follows
# init-linux-harden -username=pratik --resetroot
# init-linux-harden -u pratik --resetroot
# Change root user's password
# Choose a user name
clear
echo "Do you want to change root password ? (y/n)"
echo "(You might want to do this if you received it as an email from your host.)"
while [[ $RESET_ROOT_PWD != "y" && $RESET_ROOT_PWD != "n" ]]; do
read -rp "Select an option (y/n): " RESET_ROOT_PWD
RESET_ROOT_PWD=$(echo "$RESET_ROOT_PWD" | head -c 1)
done
function usage() {
if [ -n "$1" ]; then
echo ""
echo -e "${CRED}$1${CEND}\n"
fi
echo "Allow this script to randomly generate a username for you ? (y/n)"
while [[ $AUTO_GEN_USERNAME != "y" && $AUTO_GEN_USERNAME != "n" ]]; do
read -rp "Select an option (y/n): " AUTO_GEN_USERNAME
done
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."
if [[ $AUTO_GEN_USERNAME == 'n' ]]; then
while [[ ! "$NORM_USER_NAME" ]]; do
printf "Please provide a user name - \\n"
printf "%2s - [a-zA-Z0-9] [-] [_] are allowed\\n%2s - NO special characters.\\n%2s - NO spaces.\\n:" " " " " " "
read -r NORM_USER_NAME
# If the user exists or invalid characters - ask for a different username
if [[ $(echo "$NORM_USER_NAME" | grep -Pnc '^[a-zA-Z0-9_-]+$') -eq 0 ]] ||
[[ $(getent passwd "$NORM_USER_NAME" | wc -l) -gt 0 ]]; then
NORM_USER_NAME=""
printf "%2s !!! User name already exists or \\n%2s !!! Invalid characters in the User name.\\n" " " " "
continue
fi
done
else
echo ""
fi
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
AUTO_GEN_USERNAME="y"
RESET_ROOT_PWD="n"
DEFAULT_SOURCE_LIST="n"
while [[ "$#" -gt 0 ]]; do
case $1 in
-u|--username)
# validate username
if [[ $(echo "$2" | grep -Pnc '^[a-zA-Z0-9_-]+$') -eq 0 ]]; then
usage "Invalid characters in the user name."
exit 1
elif [[ $(getent passwd "$2" | wc -l) -gt 0 ]]; then
echo
echo -e "${CRED}User name ($2) already exists.${CEND}\n"
exit 1
else
AUTO_GEN_USERNAME="n"
NORM_USER_NAME="$2"
fi
shift
shift
;;
-r|--resetrootpwd)
RESET_ROOT_PWD="y"
shift
;;
-d|--defaultsourcelist)
DEFAULT_SOURCE_LIST="y"
shift
;;
-h|--help)
usage
shift
;;
*)
usage "Unknown parameter passed: $1" "h"
exit 1
shift
shift
;;
esac
done
##############################################################
# Log
##############################################################
CSI='\033['
CEND="${CSI}0m"
CRED="${CSI}1;31m"
CGREEN="${CSI}1;32m"
CVERTICAL="|"
CHORIZONTAL="_"
# Reset privilous log file
printf "" > "$LOGFILE"
function horizontal_fill() {
local char=$1
@@ -131,6 +165,98 @@ function recap (){
line_fill "$CVERTICAL" 1
}
function revert_changes(){
if [[ $1 = "Creating new user" ]]; then
revert_create_user
elif [[ $1 = "Creating SSH Key for new user" ]]; then
revert_create_ssh_key
elif [[ $1 = "Adding SSH Key to 'authorized_keys' file" ]]; then
revert_add_to_authorized_key
elif [[ $1 = "Securing 'authorized_keys' file" ]]; then
revert_secure_authorized_key
elif [[ $1 = "Enabling SSH-only login" ]]; then
revert_ssh_only_login
elif [[ $1 = "Changing urls in sources.list to defaults" ]]; then
# This can be reverted back individually
revert_source_list_changes
elif [[ $1 = "Installing required softwares" ]]; then
# This can be reverted back individually
return 7
elif [[ $1 = "Changing root password" ]]; then
# CANNOT be reverted back
# Just use your old password
revert_root_pass_change
fi
}
function revert_create_user(){
# Remove user and
if [[ $(getent passwd "$NORM_USER_NAME" | wc -l) -gt 0 ]]; then
deluser --remove-home "$NORM_USER_NAME"
fi
}
function revert_create_ssh_key(){
revert_create_user
KEY_FILE_BKPS=("$SSH_DIR"/"$NORM_USER_NAME".pem*"$BACKUP_EXTENSION")
if [[ ${#KEY_FILE_BKPS[@]} -gt 0 ]]; then
unalias cp
for key in "${KEY_FILE_BKPS[@]}"; do
cp -rf "$key" "${key//$BACKUP_EXTENSION/}" || exit 1
done
fi
}
function revert_add_to_authorized_key(){
revert_create_ssh_key
if [[ -f "$SSH_DIR"/authorized_keys"$BACKUP_EXTENSION" ]]; then
unalias cp
chattr -i "$SSH_DIR"/authorized_keys
#chmod 700 "$SSH_DIR"/authorized_keys
cp -rf "$SSH_DIR"/authorized_keys"$BACKUP_EXTENSION" "$SSH_DIR"/authorized_keys
chmod 400 "$SSH_DIR"/authorized_keys
chattr +i "$SSH_DIR"/authorized_keys
fi
}
function revert_secure_authorized_key(){
revert_add_to_authorized_key
}
function revert_ssh_only_login(){
revert_secure_authorized_key
if [[ -f /etc/ssh/sshd_config"$BACKUP_EXTENSION" ]]; then
unalias cp
cp -rf /etc/ssh/sshd_config"$BACKUP_EXTENSION" /etc/ssh/sshd_config
fi
}
function revert_source_list_changes(){
if [[ -f /etc/apt/sources.list"${BACKUP_EXTENSION}" ]]; then
unalias cp
cp -rf /etc/apt/sources.list"${BACKUP_EXTENSION}" /etc/apt/sources.list
fi
SOURCE_FILES_BKP=(/etc/apt/source*/*.list"${BACKUP_EXTENSION}")
if [[ ${#SOURCE_FILES_BKP[@]} -gt 0 ]]; then
unalias cp
for file in "${SOURCE_FILES[@]}";
do
cp -rf "$file" "${file//$BACKUP_EXTENSION/}" || exit 1
done
fi
}
function revert_root_pass_change(){
# If root password changed - show the new root password
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
@@ -146,10 +272,18 @@ function finally(){
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
if [[ "$#" -gt 0 ]]; then
# show - something failed - trying to restore required changes
revert_changes "$1"
# If restoration failed - well you are f**ked
fi
}
function file_log(){
printf "%s - %s\\n" "$(date '+%Y-%m-%d %H:%M:%S')" "$1" >> "$LOGFILE"
printf "%s - %s\\n" "$(date '+%d-%b-%Y %H:%M:%S')" "$1" >> "$LOGFILE"
}
function op_log() {
@@ -165,29 +299,15 @@ function op_log() {
printf "\r%30s %7s [${CRED}${RESULT}${CEND}]\\n" "$EVENT" " "
printf "\\n\\nPlease look at %s\\n\\n" "$LOGFILE"
file_log "${EVENT} - ${RESULT}"
finally "$EVENT"
else
printf "%30s %7s [${CRED}..${CEND}]" "$EVENT" " "
file_log "${EVENT} - begin..."
fi
}
declare SESSION_TYPE=""
# Check if the user connected through SSH
if [[ -n "$SSH_CLIENT" ]] || [[ -n "$SSH_TTY" ]]; then
SESSION_TYPE=remote/ssh
else
case $(ps -o comm= -p $PPID) in
sshd|*/sshd)
SESSION_TYPE=remote/ssh;
esac
fi
if [[ $SESSION_TYPE == "remote/ssh" ]]; then
file_log "Connected through SSH session."
else
file_log "Connected using password authentication."
fi
# Reset previous log file
echo "Starting $0 - $(date '+%d-%b-%Y %H:%M:%S')" > "$LOGFILE"
##############################################################
@@ -202,13 +322,13 @@ This script performs the following tasks :-
1 - Change your root password (unless you have choosen NOT to)
2 - Create a non-root user (unless provided by you a random
username will be created)
3 - Generate SSH keys on the server and store them at ~/.ssh
3 - Generate SSH keys on the server and store them at '~/.ssh'
4 - Adds the public key from the above step to
~/.ssh/authorized_keys file
5 - Restricts access to the SSH ~/.ssh folder
'~/.ssh/authorized_keys' file
5 - Restricts access to '~/.ssh' folder
6 - Restricts login method to SSH-only by editing
/etc/ssh/sshd_config file to enable
7 - Restores the /etc/apt/sources.list
'/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"
@@ -219,55 +339,30 @@ This script performs the following tasks :-
d) SSH Private Key
e) SSH Public Key
Before editing any file, script creates a back up of that file
(filename.bak) in the same directory. If script detects any
catastrophic 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.
Before editing any file, script creates a back up of that file with
in the same directory. If script detects any catastrophic 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.
All backup files have extension (${BACKUP_EXTENSION})
A log file can be found at ${LOGFILE}
TO CONTINUE (press any key)...
TO CONTINUE (press enter/return)...
TO EXIT (ctrl + c)...
INFORM
read -r
clear
##############################################################
# Change root's password
##############################################################
if [[ $RESET_ROOT_PWD == 'y' ]]; then
{
op_log "Changing root password"
# Generate a 15 character random password
PASS_ROOT="$(< /dev/urandom tr -cd "[:alnum:]" | head -c 15)" || exit 1
file_log "Generated Root Password - ${PASS_ROOT}"
# Change root's password
echo -e "${PASS_ROOT}\\n${PASS_ROOT}" | passwd
} 2>> "$LOGFILE" >&2
if [[ $? -eq 0 ]]; then
op_log "Changing root password" "SUCCESSFUL"
else
# Low priority - since we are disabling root login anyways
op_log "Changing root password" "FAILED"
fi
fi
##############################################################
# Create a normal user
# Create non-root user
##############################################################
op_log "Creating new user"
{
op_log "Creating new user"
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
file_log "Generated user name - ${NORM_USER_NAME}"
@@ -294,37 +389,42 @@ fi
##############################################################
# Create SSH Key for the new user
# Create SSH Key for the above new user
##############################################################
{
op_log "Creating SSH Key for new user"
op_log "Creating SSH Key for new user"
{
shopt -s nullglob
# TODO - Below would capture bak files as well - filter out the bak files
KEY_FILES=("$SSH_DIR"/"$NORM_USER_NAME".pem*)
#TODO - 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
for key in "${KEY_FILES[@]}"; do
cp "$key" "$key""$BACKUP_EXTENSION" || exit 1
done
fi
# Create key file only if it does NOT exist
if [[ ! ${KEY_FILES[0]} ]]; then
SSH_DIR=/home/"$NORM_USER_NAME"/.ssh
mkdir "$SSH_DIR" || exit 1
SSH_DIR=/home/"$NORM_USER_NAME"/.ssh
mkdir "$SSH_DIR" || exit 1
# Generate a 15 character random password for key
KEY_PASS="$(< /dev/urandom tr -cd "[:alnum:]" | head -c 15)" || exit 1
file_log "Generated SSH Key Passphrase - ${KEY_PASS}"
# Generate a 15 character random password for key
KEY_PASS="$(< /dev/urandom tr -cd "[:alnum:]" | head -c 15)" || exit 1
file_log "Generated SSH Key Passphrase - ${KEY_PASS}"
# 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
# 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
# See if the files actually got created
KEY_FILES=("$SSH_DIR"/"$NORM_USER_NAME".pem*)
if [[ ${#KEY_FILES[@]} -eq 0 ]]; then
file_log "Unknown error occured."
file_log "Could not create SSH key files."
exit 1
fi
# TODO - Below would capture bak files as well - filter out the bak files
# See if the files actually got created
KEY_FILES=("$SSH_DIR"/"$NORM_USER_NAME".pem*)
if [[ ${#KEY_FILES[@]} -eq 0 ]]; then
file_log "Unknown error occured."
file_log "Could not create SSH key files."
exit 1
fi
} 2>> "$LOGFILE" >&2
if [[ $? -eq 0 ]]; then
op_log "Creating SSH Key for new user" "SUCCESSFUL"
else
@@ -337,15 +437,22 @@ fi
##############################################################
# Add generated key to authorized_keys file
##############################################################
{
op_log "Adding SSH Key to 'authorized_keys' file"
# Create authorized_keys if it does not exist yet
touch "$SSH_DIR"/authorized_keys
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 "${KEY_FILES[1]}" >> "$SSH_DIR"/authorized_keys || exit 1
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
@@ -358,16 +465,23 @@ fi
##############################################################
# Secure authorized_keys file
##############################################################
{
op_log "Securing 'authorized_keys' file"
op_log "Securing 'authorized_keys' file"
{
# Set appropriate permissions for ".ssh" dir and "authorized_key" file
chown -R "$NORM_USER_NAME" "$SSH_DIR" && \
chgrp -R "$NORM_USER_NAME" "$SSH_DIR" && \
chmod 700 "$SSH_DIR" && \
chmod 400 "$SSH_DIR"/authorized_keys && \
chattr +i "$SSH_DIR"/authorized_keys
# Restrict access to the generated SSH Key files as well
for key in "${KEY_FILES[@]}"; do
chmod 400 "$key" && \
chattr +i "$key"
done
} 2>> "$LOGFILE" >&2
if [[ $? -eq 0 ]]; then
op_log "Securing 'authorized_keys' file" "SUCCESSFUL"
else
@@ -458,11 +572,10 @@ function set_config_key(){
fi
}
op_log "Enabling SSH-only login"
{
op_log "Enabling SSH-only login"
# Backup the sshd_config file
cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak || exit 1
cp /etc/ssh/sshd_config /etc/ssh/sshd_config"$BACKUP_EXTENSION" || exit 1
# Remove root login
set_config_key "/etc/ssh/sshd_config" "PermitRootLogin" "no"
@@ -475,6 +588,7 @@ function set_config_key(){
systemctl restart sshd
} 2>> "$LOGFILE" >&2
if [[ $? -eq 0 ]]; then
op_log "Enabling SSH-only login" "SUCCESSFUL"
else
@@ -488,13 +602,14 @@ fi
# Change default source-list
##############################################################
# Low priority - But what to do if it fails???
op_log "Changing urls in sources.list to defaults"
if [[ $DEFAULT_SOURCE_LIST = "y" ]]; then
# Low priority - But what to do if it fails???
op_log "Changing urls in sources.list to defaults"
mv /etc/apt/sources.list /etc/apt/sources.list.bak 2>> "$LOGFILE" >&2
sed -i "1,$(wc -l < /etc/apt/sources.list.bak) s/^/#/" /etc/apt/sources.list.bak 2>> "$LOGFILE" >&2
cp /etc/apt/sources.list /etc/apt/sources.list"${BACKUP_EXTENSION}" 2>> "$LOGFILE" >&2
sed -i "1,$(wc -l < /etc/apt/sources.list) s/^/#/" /etc/apt/sources.list 2>> "$LOGFILE" >&2
# Default sources list for debian
# Default sources list for debian
cat <<TAG > /etc/apt/sources.list || exit 1
deb https://deb.debian.org/debian ${DEB_VER_STR} main
deb-src https://deb.debian.org/debian ${DEB_VER_STR} main
@@ -511,46 +626,76 @@ deb https://deb.debian.org/debian ${DEB_VER_STR}-backports main
deb-src https://deb.debian.org/debian ${DEB_VER_STR}-backports main
TAG
# Find any additional sources listed by the provider and comment them out
SOURCE_FILES=(/etc/apt/source*/*.list) 2>> "$LOGFILE" >&2
if [[ ${#SOURCE_FILES[@]} -gt 0 ]]; then
for file in "${SOURCE_FILES[@]}";
do
mv "$file" "$file".bak 2>> "$LOGFILE" >&2
sed -i "1,$(wc -l < "$file") s/^/#/" "$file" 2>> "$LOGFILE" >&2
done
# Find any additional sources listed by the provider and comment them out
SOURCE_FILES=(/etc/apt/source*/*.list) 2>> "$LOGFILE" >&2
if [[ ${#SOURCE_FILES[@]} -gt 0 ]]; then
for file in "${SOURCE_FILES[@]}";
do
cp "$file" "$file""${BACKUP_EXTENSION}" 2>> "$LOGFILE" >&2
sed -i "1,$(wc -l < "$file") s/^/#/" "$file" 2>> "$LOGFILE" >&2
done
fi
# Comment out cloud-init generated templates for sources
# CLOUD_INIT_FILES=(/etc/cloud/templates*/*.tmpl) 2>> "$LOGFILE" >&2
# if [[ ${#CLOUD_INIT_FILES[@]} -gt 0 ]]; then
# for file in "${CLOUD_INIT_FILES[@]}";
# do
# cp "$file" "$file""${BACKUP_EXTENSION}" 2>> "$LOGFILE" >&2
# sed -i "1,$(wc -l < "$file") s/^/#/" "$file" 2>> "$LOGFILE" >&2
# done
# fi
if [[ $? -eq 0 ]]; then
op_log "Changing urls in sources.list to defaults" "FAILED"
else
op_log "Changing urls in sources.list to defaults" "FAILED"
fi
fi
# Comment out cloud-init generated templates for sources
CLOUD_INIT_FILES=(/etc/cloud/templates*/*.tmpl) 2>> "$LOGFILE" >&2
if [[ ${#CLOUD_INIT_FILES[@]} -gt 0 ]]; then
for file in "${CLOUD_INIT_FILES[@]}";
do
mv "$file" "$file".bak 2>> "$LOGFILE" >&2
sed -i "1,$(wc -l < "$file") s/^/#/" "$file" 2>> "$LOGFILE" >&2
done
fi
if [[ $? -eq 0 ]]; then
op_log "Changing urls in sources.list to defaults" "FAILED"
else
op_log "Changing urls in sources.list to defaults" "FAILED"
fi
##############################################################
# Install required softwares
##############################################################
op_log "Installing required softwares"
{
op_log "Installing required softwares"
apt-get update && apt-get upgrade -y && apt-get install -y sudo curl screen 2>> "$LOGFILE" >&2
}
apt-get update && apt-get upgrade -y && apt-get install -y sudo curl screen
} 2>> "$LOGFILE" >&2
if [[ $? -eq 0 ]]; then
op_log "Installing required softwares" "FAILED"
else
op_log "Installing required softwares" "FAILED"
fi
##############################################################
# Change root's password
##############################################################
if [[ $RESET_ROOT_PWD == 'y' ]]; then
op_log " "
{
# Generate a 15 character random password
PASS_ROOT="$(< /dev/urandom tr -cd "[:alnum:]" | head -c 15)" || exit 1
file_log "Generated Root Password - ${PASS_ROOT}"
# Change root's password
echo -e "${PASS_ROOT}\\n${PASS_ROOT}" | passwd
} 2>> "$LOGFILE" >&2
if [[ $? -eq 0 ]]; then
op_log "Changing root password" "SUCCESSFUL"
else
# Low priority - since we are disabling root login anyways
op_log "Changing root password" "FAILED"
fi
fi
##############################################################
# Recap
##############################################################