I have this bash script I've been working on and I have updated it. I was hoping for some advice and/or input on the code.
This script is run directly after a fresh install of Debian
, to do:
- Sets up syntax highlighting in
Nano
- Sets up iptables
- Sets up ssh
- Sets up custom bashrc files
ls
colors- Creates users on the system if needed
- Checks if user has a password set and sets it if not
- Installs non-free firmware and sets up
apt
withVirtualbox
deb file and multimedia deb insources.list
- Installs video and audio codecs, players and related
- Sets up
flash
forMozilla Firefox
and creates a cron for weekly updates - It updates the system
There was mention of debconf but I've never heard about that.
Could you add some features to the program that are practical, convenient or cool for setting up new installs?
Is there anything in the program that I don't need?
#!/bin/bash -x
########### Copy or Move the accompanied directory called "svaka" to /tmp ######################
################################################################################################
################## shopt (shopt [-pqsu] [-o] [optname ...]) = This builtin allows you to change additional shell optional behavior.
################## -s = Enable (set) each optname.
################## -o = Restricts the values of optname to be those defined for the -o option to the set builtin (see The Set Builtin).
################## nounset = Treat unset variables and parameters other than the special parameters ‘@’ or ‘*’ as an error when performing parameter expansion. An
# error message will be written to the standard error, and a non-interactive shell will exit.
################## The Set Builtin
#This builtin is so complicated that it deserves its own section. set allows you to change the values of shell options and set the positional parameters, or to
#display the names and values of shell variables.
shopt -s -o nounset
############################################################
#The set -e option instructs bash to immediately exit if any command [1] has a non-zero exit status. You wouldn't want to set this for your command-line shell,
#but in a script it's massively helpful. In all widely used general-purpose programming languages, an unhandled runtime error - whether that's a thrown exception
#in Java, or #a segmentation fault in C, or a syntax error in Python - immediately halts execution of the program; subsequent lines are not executed.
#set -u affects variables. When set, a reference to any variable you haven't previously defined - with the exceptions of $* and $@ - is an error, and causes the
#program to immediately exit. Languages like Python, C, Java and more all behave the same way, for all sorts of good reasons. One is so typos don't create new
#variables without you realizing it.
#set -o pipefail
#This setting prevents errors in a pipeline from being masked. If any command in a pipeline fails, that return code will be used as the return code of the whole
#pipeline. By default, the pipeline's return code is that of the last command - even if it succeeds. Imagine finding a sorted list of matching lines in a file:
# % grep some-string /non/existent/file | sort
# grep: /non/existent/file: No such file or directory
# % echo $?
# 0
#set -euo pipefail
#set -euo pipefail
#####33 Also use this↓↓↓↓↓↑↑↑↑↑↑↑↑↑↑
#set -euo pipefail
IFS_OLD=$IFS
IFS=$'\n\t'
#↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
#Setting IFS to $'\n\t' means that word splitting will happen only on newlines and tab characters. This very often produces useful splitting behavior. By default,
#bash sets this to $' \n\t' - space, newline, tab - which is too eager.
#######################↑↑↑↑↑↑↑↑
#
####### Catch signals that could stop the script
trap : SIGINT SIGQUIT SIGTERM
#################################
####################################################### Setup system to send email with your google/gmail account and sendmail ##############################
######################################################## TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO ##############################
# Configuring Gmail as a Sendmail email relay
#
#
#Introduction
#
#In this configuration tutorial we will guide you through the process of configuring sendmail to be an email relay for your gmail or google apps account.
#This allows #you to send email from your bash scripts, hosted website or from command line using mail command.
#Other examples where you can utilize this setting is for a #notification purposes such or failed backups etc.
#Sendmail is just one of many utilities which can be configured to rely on gmail account where the others include #postfix, exim , ssmpt etc.
#In this tutorial we will use Debian and sendmail for this task.
#Install prerequisites
#
## CODE:apt-get install sendmail mailutils sendmail-bin
#
#Create Gmail Authentication file
#
## CODE:mkdir -m 700 /etc/mail/authinfo/
## CODE:cd /etc/mail/authinfo/
#
#next we need to create an auth file with a following content. File can have any name, in this example the name is gmail-auth:
#
# CODE: printf 'AuthInfo: "U:root" "I:YOUR GMAIL EMAIL ADDRESS" "P:YOUR PASSWORD"\n' > gmail-auth
#
#Replace the above email with your gmail or google apps email.
#
#Please note that in the above password example you need to keep 'P:' as it is not a part of the actual password.
#
#In the next step we will need to create a hash map for the above authentication file:
#
## CODE:makemap hash gmail-auth < gmail-auth
#
#Configure your sendmail
#
#Put bellow lines into your sendmail.mc configuration file right above first "MAILER" definition line: ######################################################
#
#define(`SMART_HOST',`[smtp.gmail.com]')dnl
#define(`RELAY_MAILER_ARGS', `TCP $h 587')dnl
#define(`ESMTP_MAILER_ARGS', `TCP $h 587')dnl
#define(`confAUTH_OPTIONS', `A p')dnl
#TRUST_AUTH_MECH(`EXTERNAL DIGEST-MD5 CRAM-MD5 LOGIN PLAIN')dnl
#define(`confAUTH_MECHANISMS', `EXTERNAL GSSAPI DIGEST-MD5 CRAM-MD5 LOGIN PLAIN')dnl
#FEATURE(`authinfo',`hash -o /etc/mail/authinfo/gmail-auth.db')dnl
#############################################################################################################################################################
#Do not put the above lines on the top of your sendmail.mc configuration file !
#
#In the next step we will need to re-build sendmail's configuration. To do that execute:
#
## CODE: make -C /etc/mail
#
#Reload sendmail service:
#
# CODE:/etc/init.d/sendmail reload
#
#and you are done.
#Configuration test
#
#Now you can send an email from your command line using mail command:
#
# CODE: echo "Just testing my sendmail gmail relay" | mail -s "Sendmail gmail Relay" "This email address is being protected from spambots."
#
#######################################################3 Trap signals and exit to send email on it #######################################################
#trap 'echo "Subject: Program finsihed execution" | sendmail -v "This email address is being protected from spambots."' exit # It will mail on normal exit
#trap 'echo "Subject: Program interrupted" | /usr/sbin/sendmail -v "This email address is being protected from spambots."' INT HUP
# it will mail on interrupt or hangup of the process
# redirect all errors to a file #### MUNA setja þetta í sshd_config="#HISTAMIN98"
if [ -w /tmp/svaka ]
then
exec 2>debianConfigVersion5.3__ERRORS__.txt
else
echo "can't write error file!"
exit 127
fi
##################################################################################################### TODO exec 3>cpSuccessCodes.txt ##
#############################################################################################################
SCRIPTNAME=$(basename "0ドル")
if [ "$UID" != 0 ]
then
echo "This program should be run as root, exiting! now....."
sleep 3
exit 1
fi
if [ "$#" -eq 0 ]
then
echo "RUN AS ROOT...Usage if you want to create users:...$SCRIPTNAME USER_1 USER_2 USER_3 etc."
echo "If you create users they will be set with a semi strong password which you need to change later as root with the passwd command"
echo
echo
echo "#################### ↓↓↓↓↓↓↓↓↓↓↓ OR ↓↓↓↓↓↓↓↓↓↓ #############################"
echo
echo
echo "RUN AS ROOT...Usage without creating users: $SCRIPTNAME"
echo
sleep 10
fi
echo "Here starts the party!"
echo "Setting up server..........please wait!!!!!"
sleep 3
### ↓↓↓↓ Initialization of VARIABLES............NEXT TIME USE "declare VARIABLE" ↓↓↓↓↓↓↓↓↓↓ #####
OAUTH_TOKEN=d6637f7ccf109a0171a2f55d21b6ca43ff053616
WORK_DIR=/tmp/svaka
BASHRC=.bashrc
NANORC=.nanorc
BASHRCROOT=.bashrcroot
SOURCE=sources.list
PORT=""
########### Commands
PWD=$(pwd)
#-----------------------------------------------------------------------↓↓
export DEBIAN_FRONTEND=noninteractive
#-----------------------------------------------------------------------↑↑
################ Enter the working directory where all work happens ##########################################
cd "$WORK_DIR" || { echo "cd $WORK_DIR failed"; exit 127; }
############################### make all files writable, executable and readable in the working directory#########
if ! chown -R root:root "$WORK_DIR"
then
echo "chown WORK_DIR failed"
exit 127
fi
if ! chmod -R 750 "$WORK_DIR"
then
echo "chmod WORK_DIR failed"
exit 127
fi
############################################################## Check if files exist and are writable #########################################
if [[ ! -f "$WORK_DIR"/.bashrc && ! -w "$WORK_DIR"/.bashrc ]]
then
echo "missing .bashrc file or is not writable.. exiting now....." && { exit 127; }
fi
if [[ ! -f "$WORK_DIR"/.nanorc && ! -w "$WORK_DIR"/.nanorc ]]
then
echo "missing .nanorc file or is not writable.. exiting now....." && { exit 127; }
fi
if [[ ! -f "$WORK_DIR"/.bashrcroot && ! -w "$WORK_DIR"/.bashrcroot ]]
then
echo "missing .bashrcroot file or is not writable..exiting now....." && { exit 127; }
fi
if [[ ! -f "$WORK_DIR"/sources.list && ! -w "$WORK_DIR"/sources.list ]]
then
echo "missing sources.list file or is not writable..exiting now....." && { exit 127; }
fi
########################################### Check if PORT is set and if sshd_config is set and if PORT is set in iptables ####################
if [[ $PORT == "" ]] && ! grep -q "#HISTAMIN98" /etc/ssh/sshd_config && ! grep -q $PORT /etc/iptables.up.rules
then
echo -n "Please select/provide the port-number for ssh in iptables setup or sshd_config file:"
read -r port ### when using the "-p" option then the value is stored in $REPLY
PORT=$port
fi
############################ Check internet connection ##############################
checkInternet()
{
ping -q -w 1 -c 1 `ip r | grep default | cut -d ' ' -f 3` > /dev/null && return 0 || return 1
}
################ Creating new users #####################1
creatingNewUsers()
{
for name in "$@"
do
if id -u "$name" #>/dev/null 2>&1
then
echo "User: $name exists....setting up now!"
sleep 2
else
echo "User: $name does not exists....creating now!"
useradd -m -s /bin/bash "$name" #>/dev/null 2>&1
sleep 2
fi
done
}
###########################################################################3
################# GET USERS ON THE SYSTEM ###################################
prepare_USERS.txt()
{
awk -F: '3ドル >= 1000 { print 1ドル }' /etc/passwd > "$WORK_DIR"/USERS.txt
chmod 750 "$WORK_DIR"/USERS.txt
if [[ ! -f "$WORK_DIR"/USERS.txt && ! -w "$WORK_DIR"/USERS.txt ]]
then
echo "USERS.txt doesn't exist or is not writable..exiting!"
sleep 3
exit 127
fi
# if [[ ! "$@" == "" ]]
# then
# for user in "$@"
# do
# echo "$user" >> /tmp/svaka/USERS.txt || { echo "writing to USERS.txt failed"; exit 127; }
# done
# fi
}
###########################################################################33
################33 user passwords2
userPasswords()
{
if [[ ! -f "$WORK_DIR"/USERS.txt && ! -w "$WORK_DIR"/USERS.txt ]]
then
echo "USERS.txt doesn't exist or is not writable..exiting!"
sleep 3
exit 127
fi
while read -r user
do
if [ "$user" = root ]
then
continue
fi
if [[ $(passwd --status "$user" | awk '{print 2ドル}') = NP ]] || [[ $(passwd --status "$user" | awk '{print 2ドル}') = L ]]
then
echo "$user doesn't have a password."
echo "Changing password for $user:"
sleep 3
echo "$user":"$user""YOURSTRONGPASSWORDHERE12345Áá" | /usr/sbin/chpasswd
if [ "$?" = 0 ]
then
echo "Password for user $user changed successfully"
sleep 3
fi
fi
done < "$WORK_DIR"/USERS.txt
}
################################################ setting up iptables ####################3
setUPiptables()
{
#if ! grep -e '-A INPUT -p tcp --dport 80 -j ACCEPT' /etc/iptables.test.rules
if [[ $(/sbin/iptables-save | grep -c '^\-') -gt 0 ]]
then
echo "Iptables already set, skipping..........!"
sleep 2
else
if [ "$PORT" = "" ]
then
echo "Port not set for iptables, setting now......."
echo -n "Setting port now, insert portnumber: "
read -r port
PORT=$port
fi
if [ ! -f /etc/iptables.test.rules ]
then
touch /etc/iptables.test.rules
else
cat /dev/null > /etc/iptables.test.rules
fi
cat << EOT >> /etc/iptables.test.rules
*filter
# Allows all loopback (lo0) traffic and drop all traffic to 127/8 that doesn't use lo0
-A INPUT -i lo -j ACCEPT
-A INPUT ! -i lo -d 127.0.0.0/8 -j REJECT
# Accepts all established inbound connections
-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
# Allows all outbound traffic
# You could modify this to only allow certain traffic
-A OUTPUT -j ACCEPT
# Allows HTTP and HTTPS connections from anywhere (the normal ports for websites)
-A INPUT -p tcp --dport 80 -j ACCEPT
-A INPUT -p tcp --dport 443 -j ACCEPT
# Allows SSH connections
# The --dport number is the same as in /etc/ssh/sshd_config
-A INPUT -p tcp -m state --state NEW --dport $PORT -j ACCEPT
# Now you should read up on iptables rules and consider whether ssh access
# for everyone is really desired. Most likely you will only allow access from certain IPs.
# Allow ping
# note that blocking other types of icmp packets is considered a bad idea by some
# remove -m icmp --icmp-type 8 from this line to allow all kinds of icmp:
# https://security.stackexchange.com/questions/22711
-A INPUT -p icmp -m icmp --icmp-type 8 -j ACCEPT
# log iptables denied calls (access via dmesg command)
-A INPUT -m limit --limit 5/min -j LOG --log-prefix "iptables denied: " --log-level 7
# Reject all other inbound - default deny unless explicitly allowed policy:
-A INPUT -j REJECT
-A FORWARD -j REJECT
COMMIT
EOT
sed "s/^[ \t]*//" -i /etc/iptables.test.rules ## remove tabs and spaces
/sbin/iptables-restore < /etc/iptables.test.rules || { echo "iptables-restore failed"; exit 127; }
/sbin/iptables-save > /etc/iptables.up.rules || { echo "iptables-save failed"; exit 127; }
printf "#!/bin/bash\n/sbin/iptables-restore < /etc/iptables.up.rules" > /etc/network/if-pre-up.d/iptables ## create a script to run iptables on startup
chmod +x /etc/network/if-pre-up.d/iptables || { echo "chmod +x failed"; exit 127; }
fi
}
###################################################33 sshd_config4
setUPsshd()
{
if grep "Port $PORT" /etc/ssh/sshd_config
then
echo "sshd already set, skipping!"
sleep 3
else
if [ "$PORT" = "" ]
then
echo "Port not set"
sleep 3
exit 12
fi
users=""
/bin/cp -f "$WORK_DIR"/sshd_config /etc/ssh/sshd_config
sed -i "s/Port 22300/Port $PORT/" /etc/ssh/sshd_config
for user in $(awk -F: '3ドル >= 1000 { print 1ドル }' /etc/passwd)
do
users+="${user} "
done
if grep "AllowUsers" /etc/ssh/sshd_config
then
sed -i "/AllowUsers/c\AllowUsers $users" /etc/ssh/sshd_config
else
sed -i "6 a \
AllowUsers $users" /etc/ssh/sshd_config
fi
chmod 644 /etc/ssh/sshd_config
/etc/init.d/ssh restart
fi
}
#################################################3333 Remove or comment out DVD/cd line from sources.list5
editSources()
{
if grep '^# *deb cdrom:\[Debian' /etc/apt/sources.list
then
echo "cd already commented out, skipping!"
else
sed -i '/deb cdrom:\[Debian GNU\/Linux/s/^/#/' /etc/apt/sources.list
fi
}
####################################################33 update system6
updateSystem()
{
apt update && apt upgrade -y
}
###############################################################7
############################# check if programs installed and/or install
checkPrograms()
{
if [ ! -x /usr/bin/git ] && [ ! -x /usr/bin/wget ] && [ ! -x /usr/bin/curl ] && [ ! -x /usr/bin/gcc ] && [ ! -x /usr/bin/make ]
then
echo "Some tools with which to work with data not found installing now......................"
sleep 2
apt install -y git wget curl gcc make
fi
}
#####################################################3 update sources.list and install software ############################################################
updateSources_installSoftware()
{
if grep "deb http://www.deb-multimedia.org" /etc/apt/sources.list
then
echo "Sources are setup already, skipping!"
else
/bin/cp -f "$WORK_DIR"/"$SOURCE" /etc/apt/sources.list || { echo "cp failed"; exit 127; }
chmod 644 /etc/apt/sources.list
wget http://www.deb-multimedia.org/pool/main/d/deb-multimedia-keyring/deb-multimedia-keyring_2016年8月1日_all.deb || { echo "wget failed"; exit 127; }
dpkg -i deb-multimedia-keyring_2016年8月1日_all.deb
wget -q https://www.virtualbox.org/download/oracle_vbox_2016.asc -O- | sudo apt-key add -
updateSystem || { echo "update system failed"; exit 127; }
apt install -y vlc vlc-data browser-plugin-vlc mplayer youtube-dl libdvdcss2 libdvdnav4 libdvdread4 smplayer mencoder build-essential \
gstreamer1.0-libav gstreamer1.0-plugins-bad gstreamer1.0-vaapi lame libfaac0 aacskeys libbdplus0 libbluray1 audacious audacious-plugins \
deadbeef kodi audacity cinelerra handbrake-gtk ffmpeg amarok k3b || { echo "some software failed to install!!!!!"; echo "some software failed to install"; \
sleep 10; }
########################## Install flash in Mozilla Firefox ############################################
wget https://raw.githubusercontent.com/cybernova/fireflashupdate/master/fireflashupdate.sh || { echo "wget flash failed"; sleep 4; exit 127; }
chmod +x fireflashupdate.sh || { echo "chmod flash failed"; sleep 4; exit 127; }
./fireflashupdate.sh
######################### Setup the update tool to update flash weekly ###################################3
chown root:root fireflashupdate.sh || { echo "chown flash failed"; sleep 4; exit 127; }
/bin/mv fireflashupdate.sh /etc/cron.weekly/fireflashupdate || { echo "mv flash script failed"; sleep 4; exit 127; }
fi
}
###############################################33 SETUP PORTSENTRY ############################################################
##############################################3 ############################################################33
setup_portsentry()
{
if ! grep -q '^TCP_PORTS="1,7,9,11,15,70,79' /etc/portsentry/portsentry.conf
then
if [[ -f /etc/portsentry/portsentry.conf ]]
then
/bin/mv /etc/portsentry/portsentry.conf /etc/portsentry/portsentry.old
fi
if [[ ! -x /usr/sbin/portsentry ]]
then
apt install -y portsentry logcheck
/bin/cp -f "$WORK_DIR"/portsentry.conf /etc/portsentry/portsentry.conf || { echo "cp portsentry failed"; exit 127; }
/usr/sbin/service portsentry restart || { echo "service portsentry restart failed"; exit 127; }
fi
fi
}
################################### Successful exit then this cleanup ###########################################################3
successfulExit()
{
IFS=$IFS_OLD
cd "$HOME" || { echo "cd $HOME failed"; exit 155; }
rm -rf /tmp/svaka || { echo "Failed to remove the install directory!!!!!!!!"; exit 155; }
}
###############################################################################################################################33
####### Catch the program on successful exit and cleanup
trap successfulExit EXIT
#####################################################3 run methods here↓ ###################################################3
##################################################### ###################################################
checkInternet || (echo "no network, bye" && exit 199)
if [[ ! "$*" == "" ]]
then
creatingNewUsers "$@"
fi
prepare_USERS.txt
userPasswords
setUPiptables
setUPsshd
editSources
updateSystem
#setup_portsentry ######3 NEEDS WORK ##################################
checkPrograms
updateSources_installSoftware
########################################################################################################### #####3##
##############################################################################################################3Methods
##########################################3 Disable login for www-data #########
passwd -l www-data
#################################### firmware
apt install -y firmware-linux-nonfree firmware-linux
apt install -y firmware-linux-free intel-microcode
sleep 3
################ NANO SYNTAX-HIGHLIGHTING #####################3
if [ ! -d "$WORK_DIR"/nanorc ]
then
if [ "$UID" != 0 ]
then
echo "This program should be run as root, goodbye!"
exit 127
else
echo "Setting up Nanorc file for all users....please, wait!"
if [[ $PWD == "$WORK_DIR" ]]
then
echo "Program is in WORK_DIR...success!......."
else
echo "not in WORK_DIR...TRYING 'cd WORK_DIR'"
cd "$WORK_DIR" || { echo "cd failed"; exit 127; }
fi
git clone https://$OAUTH_TOKEN:[email protected]/gnihtemoSgnihtemos/nanorc || { echo "git in Nano SYNTAX-HIGHLIGHTING failed"; exit 127; }
chmod 755 "$WORK_DIR"/nanorc || { echo "chmod in Nano SYNTAX-HIGHLIGHTING failed"; exit 127; }
cd "$WORK_DIR"/nanorc || { echo "cd in Nano SYNTAX-HIGHLIGHTING failed"; exit 127; }
make install-global || { echo "make in Nano SYNTAX-HIGHLIGHTING failed"; exit 127; }
/bin/cp -f "$WORK_DIR/$NANORC" /etc/nanorc || { echo "cp in Nano SYNTAX-HIGHLIGHTING failed"; exit 127; }
chown root:root /etc/nanorc || { echo "chown in Nano SYNTAX-HIGHLIGHTING failed"; exit 127; }
chmod 644 /etc/nanorc || { echo "chmod in Nano SYNTAX-HIGHLIGHTING failed"; exit 127; }
if [ "$?" = 0 ]
then
echo "Implementing a custom nanorc file succeeded!"
else
echo "Nano setup DID NOT SUCCEED!"
exit 127
fi
echo "Finished setting up nano!"
fi
fi
################ LS_COLORS SETTINGS and bashrc file for all users #############################
if ! grep 'eval $(dircolors -b $HOME/.dircolors)' /root/.bashrc
then
echo "Setting root bashrc file....please wait!!!!"
if /bin/cp -f "$WORK_DIR/$BASHRCROOT" "$HOME"/.bashrc
then
echo "Root bashrc copy succeeded!"
sleep 2
else
echo "Root bashrc cp failed, exiting now!"
exit 127
fi
chown root:root "$HOME/.bashrc" || { echo "chown failed"; exit 127; }
chmod 644 "$HOME/.bashrc" || { echo "failed to chmod"; exit 127; }
wget https://raw.github.com/trapd00r/LS_COLORS/master/LS_COLORS -O "$HOME"/.dircolors || { echo "wget failed"; exit 127; }
echo 'eval $(dircolors -b $HOME/.dircolors)' >> "$HOME"/.bashrc || { echo "echo 'eval...dircolors -b'....to bashrc failed"; exit 127; }
fi
while read -r user
do
if [ "$user" = root ]
then
continue
fi
sudo -i -u "$user" user="$user" WORK_DIR="$WORK_DIR" BASHRC="$BASHRC" bash <<'EOF'
if grep 'eval $(dircolors -b $HOME/.dircolors)' "$HOME"/.bashrc
then
:
else
echo "Setting users=Bashrc files!"
if /bin/cp -f "$WORK_DIR"/"$BASHRC" "$HOME/.bashrc"
then
echo "Copy for $user (bashrc) succeeded!"
sleep 2
else
echo "Couldn't cp .bashrc for user $user"
exit 127
fi
chown $user:$user "$HOME/.bashrc" || { echo "chown failed"; exit 127; }
chmod 644 "$HOME/.bashrc" || { echo "chmod failed"; exit 127; }
wget https://raw.github.com/trapd00r/LS_COLORS/master/LS_COLORS -O "$HOME"/.dircolors || { echo "wget failed"; exit 127; }
echo 'eval $(dircolors -b $HOME/.dircolors)' >> "$HOME"/.bashrc
fi
EOF
done < "$WORK_DIR"/USERS.txt
echo "Finished setting up your system!"
sleep 2
############ Give control back to these signals
trap SIGINT SIGQUIT SIGTERM
############################
exit 0
Here is the same program under development posted here 2 times in the past:
Bash script to setup new Debian installs.......from 7 months ago
1 Answer 1
Got a snack and drink? Probably a good idea to get comfy too because it's going to be another one of those answers.
Before diving in though understand that I'm sharing what I know and think will be helpful, there's defiantly more clever people out there with Bash, and perhaps those that are will be kind enough to let us all know if any of the following would lead readers astray.
"Could you add some features to the program that are practical, convenient or cool for setting up new installs?"
Yes, however I don't think either of us would want to look at it after...
Addressing some of your list;
Abandon customizing
nano
and instead focus onvim
, be very targeted about plugins and customizing, and try to focus on learning the built in goodies to tweak. For examplevimdiff
as a merge tool forgit
is super handy for remote (or local) merge conflict resolution, andvim-gpg
allows for editing encrypted files in-place. Combined with eitherscreen
ortmux
and it's possible to have a similar (or better) experience as whatIDEs
likeAtom
provides.iptables
andssh
setup is something that should be handled by separate scripts at the very least, and I'll reiterate thatsystemd
combined withiptables
is an avenue worth exploring... may be in the future I'llpush
a related project, when it's ready...- Additionally on
iptables
, look intochains
, andinsert
to keep things organized and in somewhat predictable order respectively. Also check intofail2ban
for an easy way of mitigating some forms of hacktackory, it also has templating options for automating changes in rules based off various logged events.
- Additionally on
Careful, setting-up custom root account
bashrc
files, especially for a shared server this can cause strangeness in terminal behavior, which can strain team relations, that strain could lead to anger, and Yoda was very clear to where such emotions leads.Tweaking default color output for
ls
and just about any other core utility is a very, very bad idea... well unless tracking down the legacy code underpinning something server critical that barfed is your idea of a good time... Hint, if it's in the Busybox Bash shell avoid touching the defaults on any distribution. If you must, for some reason, then please use uniquely namedaliases
instead... as for why, detecting reliably when something is being piped or redirected or displayed to a terminal that can handle it is a special flavor of mysticism I'll not encourage publicly; not for this use-case...Creating new users, set-up, passwords, etc. is something that yet again should be handled by a separate set of scripts. Additionally be careful as to who you assign a password to, could allow for a larger pool of users to brute-force passwords for, and some users should never have a password but instead be locked from these types of authentication.
Installation of non-free software in an automated fashion may be asking for trouble, I'm no expert but such habits will be very tough to maintain in a corporate setting. In other-words be really nice to the Waver Department for whatever company lets ya
sudo
onto their servers.I've already touched on that auto upgrading a system is dangerous, but if you must then consider using
script
so that issues can later be tracked down with the use ofstrings
, eg...
mkdir -p ~/Documents/logs/script
_a_moment_in_time="$(date +'%Y%m%d-%H%M%S')"
_log_path="${HOME}/Documents/logs/script/${_a_moment_in_time}-apt-get_upgrade.script"
script -ac 'sudo apt-get upgrade && sudo apt-get upgrade -y' "${_log_path}"
... to output a script
log file to something like ~/Documents/logs/script/19701231-2359-apt-get_upgrade.script
, which can be reviewed in-terminal with something like...
strings "${_log_path}" | more
"Is there anything in the program that I don't need?"
Yes.
"I was hoping for some advice and/or input on the code."
Split out things into separate projects, and within those projects split things out into smaller files of functions that contain reusable code snipits; your future self will be more likely to thank your past self.
Comments should be comments, documentation should either be accessible via some state within the code or be kept in a different place; Python is a good example of where it's encouraged to have documentation within the code but that's because
print(str.__doc__)
.There's some unnecessary redundant repetition in your code that might be better expressed as a collection of functions that abstract things a bit in that regard. On the subject of functions, some of your names are going to cause ya headaches in the future, I'm looking at
prepare_USERS.txt
as one that'll one day misbehave.There's lot's of zombie code and characters that cannot be typed on a keyboard, consider removing the latter and editing the former to something either with; conditional logic and configs, or removing unnecessary commented code.
Functions and scripts can take a list of arguments, functions can also be passed references to lists (
arrays
) among other things vialocal -n _ref_the_first="${1}"
, which hint, hint, is magic sauce for that project'sarg_parser
that I linked to in your question's comments; enables passing more than onearray
;-)Definitely look into
shellcheck
as it'll catch typos likeif [ "$PORT" = "" ]
in your code, output the line number to jump to to fix'em, as well as a message that may or may not be helpful at the time.On the subject of
help
related things, I'll encourage you to tryman
, egman man
,man iptables
, etc. The documentation built-in on various subjects is extensive. The other handy command I've run across for finding documentation isapropos
, eg.apropos iptables
trap
ing some of the things you have is asking for trouble in some very bad ways, and othertrap
s are going to be really painful to debug in the future, consider using some bits that my project does to aid debugging instead, eg...
shared_functions/failure.sh
Note, source code within links may be updated or slightly different in formatting, if this is by to much at some later date then roll-back changes to commit ID:
5d307b353fc83cfc3d51461a064e72cdb8f4176a
; currently shared under GNU AGPLversion 3
.
#!/usr/bin/env bash
## Ensure the following line precedes calls to this function via trap
## set -eE -o functrace
## Use the following line to call this function from other scripts
## trap 'failure ${LINENO} "${BASH_COMMAND}"' ERR
failure(){
local _lineno="${1}"
local _msg="${2}"
local _code="${3:-0}"
local _msg_height="$(wc -l <<<"${_msg}")"
if [ "${_msg_height}" -gt '1' ]; then
printf 'Line: %s\n%s\n' "${_lineno}" "${_msg}"
else
printf 'Line: %s - %s\n' "${_lineno}" "${_msg}"
fi
if ((_code)); then exit ${_code}; fi
return ${_code}
}
errors_tests.sh
pulled from jekyll_usermod.sh
Trimmed a bit to keep things a bit targeted in topics being covered.
#!/usr/bin/env bash
## Exit if not running with root/level permissions
if [[ "${EUID}" != '0' ]]; then
echo "Try: sudo ${0##*/} ${@:---help}"
exit 1
fi
set -eE -o functrace
#
# Set defaults for script variables; these maybe overwritten at run-time
#
## ... trimmed for brevity...
#
# Boilerplate and script scoped defaults
#
## Find true directory script resides in, true name, and true path
__SOURCE__="${BASH_SOURCE[0]}"
while [[ -h "${__SOURCE__}" ]]; do
__SOURCE__="$(find "${__SOURCE__}" -type l -ls | sed -n 's@^.* -> \(.*\)@1円@p')"
done
__DIR__="$(cd -P "$(dirname "${__SOURCE__}")" && pwd)"
__NAME__="${__SOURCE__##*/}"
__PATH__="${__DIR__}/${__NAME__}"
__AUTHOR__='S0AndS0'
__DESCRIPTION__='Spams error codes for trap example'
#
# Source useful functions, note may overwrite previous defaults
#
## Provides 'failure'
source "${__DIR__}/shared_functions/failure.sh"
trap 'failure ${LINENO} "${BASH_COMMAND}"' ERR
## ... sourcing of other things, setup and
## code logic/execution follows for now
## generate some errors...
usage(){
cat <<EOF
${__NAME__} 'SPAM'
${__DESCRIPTION__}
--help
Print this message and exit
spam
Returns two different error codes, repetition dependent
ham
Returns error code '3'
# (C) GNU AGPL version 3, 2019, ${__AUTHOR__}
EOF
}
spam_errors(){
_first_arg="${1,,}"
case "${_first_arg}" in
'spam')
_scrubbed_first_arg="${_first_arg//${_first_arg/%spam/}/ }"
printf '%s\n' "${_scrubbed_first_arg}" >&2
return 1
;;
'spam'*'spam')
_scrubbed_first_arg="${_first_arg//${_first_arg//spam/}/ }"
printf '%s\n' "${_scrubbed_first_arg}" >&2
return 2
;;
*'ham'*)
printf '%s\n' "${_first_arg}" >&2
return 3
;;
*'help')
usage
exit 0
;;
esac
return 0
}
#
# Do the things...
#
_first_arg="${1:?${__NAME__} requires one argument, eg. ${__NAME__} 'spam'}"
spam_errors "${_first_arg}"
printf 'Escaped argument: %q\n' "${_first_arg}"
Note,
spam_errors
only exists for the purpose of showing multiple error generating lines as well as what happens when no errors are thrown.
... the gist of which prints out information that helps in tracking down errors. Here's some example input/output...
bash errors_tests.sh 'spam spam'; echo "# Exit Status: $?"
Try: sudo errors_tests.sh spam spam
# Exit Status: 1
sudo bash errors_tests.sh
echo "# Exit Status: $?"
errors_tests.sh: line 71: 1: errors_tests.sh requires one argument, eg. errors_tests.sh spam
# Exit Status: 1
sudo bash errors_tests.sh 'spam'; echo "# Exit Status: $?"
spam
Line: 52 - return 1
# Exit Status: 1
sudo bash errors_tests.sh 'spam ham spam'
_last_exit_status="$?"
echo "# Exit Status: ${_last_exit_status:-0}"
spam spam
Line: 57 - return 2
# Exit Status: 2
sudo bash errors_tests.sh 'ham'
_last_exit_status="${?:-0}"
echo "# Exit Status: ${_last_exit_status}"
ham
Line: 62 - return 3
# Exit Status: 3
sudo bash errors_tests.sh 'lemon jam'
_last_exit_status="$?"
echo "# Exit Status: ${_last_exit_status:-0}"
Escaped argument: lemon\ jam
# Exit Status: 0
Now there's other things going on up there with them examples; string manipulation, fancy source
usage based-off something I couldn't unsee from elsewhere on the SO network, all sorts of perversions with parameter substitutions; but the main take-away to take away is that I'm using return
n
>
0
within functions to trip the trap 'failure ${LINENO} "${BASH_COMMAND}"' ERR
, after setting set -eE -o functrace
for more info on premature exits. Other things I think may be worth noting;
In some implementations of Bash each pipe can be very costly, so try to get things done with built-ins where-ever possible.
Regardless of language, erring with info is a solid choice.
echo
is okay to use in the privacy of one's own terminal, and occasionally out of laziness when no variables are involved within a script, otherwiseprintf
is generally the better choice.That trick within the
usage
function you might see elsewhere with-EOF
which dedents tabs (\t
), or'EOF'
to forbid expansion of variables ($var
) and sub-shells ($(ls some-dir)
); the latter is groovy to use when you don't want to escape (pre'ppend\
) a bunch of examples, where as the former should be avoided to keep code accessible.
I could probably go-on, maybe I will in a future update, but for now I think this is a good pausing point to digest some of the above.
bash
/shell scripting
andpython
now, and plan on learningC
andC++
later \$\endgroup\$iptables
integration, look intosystemd
for automation. Also specific to your code, consider turning individual bits into scripts; it'll make setting expectations and testing far easier. Furthermore look intosource
and how it can break out portions of scripted logic into more manageable chunks. Some things do not need a wrapper, specificallyupdateSystem
is unnecessary and dangerous. Finally I'd advise looking into a good IDE,vim
andAtom
are my favs, but there's plenty of good text editors that'll make your life way easier. \$\endgroup\$Git
pane, if ya look at the lower bar of yourAtom
window (on the right side), you'll find some otherGit
related short-cuts, eg.branch
selector. On the subject of both Bash and administration ya may want to peruse the source code of a project I've recently published for Jekyll Administration, that also includes some swell client scripts; it's designed to be a set ofssh
short-cuts for the most part. Though I mainly suggest it because there's lot's of Bash tricks that ya might like. \$\endgroup\$