Last active
November 26, 2021 00:53
-
-
Save pbkwee/f819723004a38839bbd1e7d03fe260a3 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# | |
# Copyright Rimuhosting.com | |
function usage { | |
echo " | |
$0 Creates a backup of a Linux server. It has options to let you download that via http (else you can scp it from the source). It has options to encrypt the backup file (e.g. via openssl or zip). | |
Usage: $0 | |
--encrypt openssl (default if using --http) | zip (not so secure) | none (default if not using --http) | |
--http (serve file on an http url) | |
--files (default to / ) | |
--size output the size of the backup (without creating it or using any disk space) | |
--outputdir output directory [ /root/backup.s2i ] | |
--outputfile output file [ backup-$dt ] | |
--outputextn output file extension [ gz | zip | gz.enc depending on encryption ] | |
--outputpath output file full path (overrides other output options) | |
--password by default we will create a password for you. And use the same password each time the same outputdir is used. | |
We recommend you stop database servers and other processes that may be updating files while you run this script. | |
If you use the --http option we will put the file on a URL that should be secret. However we still recommend you use one of the --encrypt options. | |
" | |
return 0 | |
} | |
# date like 2021-06-28-1624846640 | |
dt="$(date +%Y-%m-%d-%s)" | |
# openssl, none, zip | |
encrypt="" | |
ishttp="" | |
files=/ | |
issize="" | |
outputdir="${outputdir:-/root/backup.s2i}" | |
outputfile="${outputfile:-backup-$dt}" | |
function parse() { | |
while [ -n "$1" ]; do | |
case "$1" in | |
--encrypt) | |
shift | |
if [ -z "$1" ]; then | |
echo "Missing --encrypt type" >&2 | |
usage | |
return 1 | |
elif [ "$1" == "openssl" ] ; then | |
encrypt="openssl" | |
elif [ "$1" == "zip" ]; then | |
encrypt="zip" | |
elif [ "$1" == "none" ]; then | |
encrypt="none" | |
else | |
echo "Unrecognized --encrypt type '$1'" >&2 | |
usage | |
return 1 | |
fi | |
;; | |
--outputdir) | |
shift | |
[ -z "$1" ] && echo "Missing output dir" >&2 && return 1 | |
[ -e "$1" ] && [ ! -d "$1" ] && echo "Output dir must be a directory" >&2 && return 1 | |
outputdir="$1" | |
;; | |
--password) | |
shift | |
[ -z "$1" ] && echo "Missing password" >&2 && return 1 | |
password="$1" | |
;; | |
--outputfile) | |
shift | |
[ -z "$1" ] && echo "Missing output file" >&2 && return 1 | |
[ -e "$1" ] && [ ! -f "$1" ] && echo "Output file must be a file" >&2 && return 1 | |
outputfile="$1" | |
;; | |
--outputextn) | |
shift | |
[ -z "$1" ] && echo "Missing output extn" >&2 && return 1 | |
outputextn="$1" | |
;; | |
--outputpath) | |
shift | |
[ -z "$1" ] && echo "Missing output path" >&2 && return 1 | |
[ -z "$(dirname $1)" ] && echo "Need a full path and filename for outputpath" >&2 && return 1 | |
[ "." == "$(dirname $1)" ] && echo "Need a full path and filename for outputpath" >&2 && return 1 | |
outputpath="$1" | |
;; | |
--size) | |
issize="xxx" | |
;; | |
--files) | |
shift | |
if [ -z "$1" ]; then | |
echo "Missing --files argument" >&2 | |
usage | |
return 1 | |
fi | |
files="$1" | |
for i in $files; do | |
[ ! -e $i ] && echo "No such file as $i" >&2 && return 1 | |
done | |
;; | |
--http) | |
ishttp="xxx" | |
;; | |
-? | --help) | |
usage | |
return 1 | |
;; | |
*) | |
echo "Unrecognized parameter '$1'" >&2 | |
usage | |
return 1; | |
;; | |
esac | |
shift | |
done | |
} | |
# rsync typically needed on the restore side | |
if ! which rsync 2>&1 1>/dev/null; then | |
which apt-get 2>&1 1>/dev/null && apt-get -y install rsync | |
fi | |
if ! which tar 2>&1 1>/dev/null; then | |
echo "tar not installed." >&2 | |
exit 1 | |
fi | |
parse $* | |
[ $? -ne 0 ] && exit 1 | |
[ -z "$ishttp" ] && [ -z "$encrypt" ] && encrypt="none" && echo "Not using http, not doing backup file encryption" | |
[ ! -z "$ishttp" ] && [ -z "$encrypt" ] && encrypt="openssl" && echo "Using http, enabling backup file openssl encryption" | |
if [ "$encrypt" == "openssl" ]; then | |
outputextn="${outputextn:-gz.enc}" | |
elif [ "$encrypt" == "zip" ]; then | |
outputextn="${outputextn:-zip}" | |
elif [ "$encrypt" == "none" ]; then | |
outputextn="${outputextn:-gz}" | |
fi | |
outputpath="${outputpath:-$outputdir/$outputfile.$outputextn}" | |
# create a backup directory | |
[ ! -d "$(dirname $outputpath)" ] && mkdir -p "$(dirname $outputpath)" | |
if [ -z "$password" ]; then | |
# random password of letters and digits | |
[ ! -f "$(dirname $outputpath)/.bupassword" ] && </dev/urandom tr -dc A-Z0-9 | head -c10 > "$(dirname $outputpath)/.bupassword" | |
password="$(cat "$(dirname $outputpath)/.bupassword")" | |
else | |
echo "$password" > "$(dirname $outputpath)/.bupassword" | |
fi | |
#cd "$(dirname $outputpath)" | |
# exclude mysql and log files, but keep directory structure | |
[ -d /var/log ] && find /var/log -type f > $(dirname $outputpath)/excludefiles.log | |
[ -d /var/cache/apt/archives ] && find /var/cache/apt/archives -type f >> $(dirname $outputpath)/excludefiles.log | |
touch $(dirname $outputpath)/excludefiles2.log | |
#find /var/lib/mysql -type f > "$(dirname $outputpath)/excludefiles.log" | |
# exclude sockets | |
find / -type s -print 2>/dev/null >> "$(dirname $outputpath)/excludefiles.log" | |
# create a tar file, exclude certain directories | |
# encrypt the data using openssh with the provided password | |
taropts="--numeric-owner --create --preserve-permissions --gzip --file - | |
--exclude-from=$(dirname $outputpath)/excludefiles.log | |
--exclude-from=$(dirname $outputpath)/excludefiles2.log | |
--exclude=$(dirname $outputpath) | |
--exclude=/root/backup.* | |
--exclude=/restore* | |
--exclude=/proc | |
--exclude=/tmp | |
--exclude=/mnt | |
--exclude=/dev | |
--exclude=/sys | |
--exclude=/run | |
--exclude=/media | |
--exclude=/usr/src/linux-headers* | |
--exclude=/home/*/.gvfs | |
--exclude=/home/*/.cache | |
--exclude=/home/*/.local/share/Trash $files" | |
ip="$(ifconfig eth0 | grep 'inet ' | sed 's/inet addr:/inet /' | awk '{print $2}')" | |
[ -z "$ip" ] && echo "Could not determine IP address." >&2 && exit 1 | |
echo "Starting server-to-image at $dt" | tee "$(dirname $outputpath)/.buinstructions" | |
if [ ! -z "$issize" ]; then | |
echo "Checking the file size of the backup..." | tee -a "$(dirname $outputpath)/.buinstructions" | |
if [ "$encrypt" == "openssl" ]; then | |
bytes="$(tar $taropts | openssl enc -aes-256-cbc -md sha256 -pass "pass:$password" | wc -c)" | |
elif [ "$encrypt" == "zip" ]; then | |
bytes="$(tar $taropts | zip --encrypt --password "$password" | wc -c)" | |
elif [ "$encrypt" == "none" ]; then | |
bytes="$(tar $taropts | wc -c)" | |
else | |
bytes="NA" | |
fi | |
echo "The backup size is $bytes bytes $(which numfmt 2>&1 >/dev/null && numfmt --to=iec-i --suffix=B --padding=7 $bytes)" | tee -a "$(dirname $outputpath)/.buinstructions" | |
exit 0 | |
fi | |
ret=0 | |
if [ "$encrypt" == "openssl" ]; then | |
echo "Creating tar file, openssl encrypted, at $outputpath" | tee -a "$(dirname $outputpath)/.buinstructions" | |
tar $taropts | openssl enc -aes-256-cbc -md sha256 -pass "pass:$password" > $outputpath | |
RC=( "${PIPESTATUS[@]}" ) | |
[ ${RC[0]} -ne 0 ] || [ ${RC[1]} -ne 0 ] && ret=1 | |
echo "OpenSSL command to decrypt the $outputpath backup file openssl enc -d -aes-256-cbc -md sha256 -pass "pass:$password" -in $outputpath -out backup.tar.gz" | tee -a "$(dirname $outputpath)/.buinstructions" | |
elif [ "$encrypt" == "zip" ]; then | |
echo "Creating zip file, password encrypted (password is $password), at $outputpath" | tee -a "$(dirname $outputpath)/.buinstructions" | |
tar $taropts | zip --encrypt --password "$password" > $outputpath | |
RC=( "${PIPESTATUS[@]}" ) | |
[ ${RC[0]} -ne 0 ] || [ ${RC[1]} -ne 0 ] && ret=1 | |
elif [ "$encrypt" == "none" ]; then | |
echo "Creating tar file, not encrypted, at $outputpath" | tee -a "$(dirname $outputpath)/.buinstructions" | |
tar $taropts > $outputpath | |
ret=$? | |
else | |
echo "Unexpected encryption type '$encrypt'" >&2 | |
exit 1 | |
fi | |
[ $ret -ne 0 ] && echo "Backup creation failed.">&2 && exit 1 | |
echo "Backup created: $(ls -lh $outputpath)" | tee -a "$(dirname $outputpath)/.buinstructions" | |
# record the full path of the output | |
echo $outputpath > "$(dirname $outputpath)/.outputpath" | |
secretdirname="$(</dev/urandom tr -dc A-Z0-9 | head -c10)" | |
if [ ! -z "$ishttp" ]; then | |
mkdir -p "$(dirname $outputpath)/$secretdirname" | |
mv $outputpath "$(dirname $outputpath)/$secretdirname/" | |
newoutputpath="$(dirname $outputpath)/$secretdirname/$(basename $outputpath)" | |
echo "Moving the output file from $outputpath file to $newoutputpath, to improve HTTP security" | tee -a "$(dirname $outputpath)/.buinstructions" | |
outputpath="$newoutputpath" | |
# offer the file for download. Kill this process off after you have downloaded the file. | |
# PHP has a built in web server | |
# at job to kill off process after 24h? | |
nohup php -S $ip:32956 & | |
echo "Download your backup from http://$ip:32956/$secretdirname/$(basename $outputpath)" | tee -a "$(dirname $outputpath)/.buinstructions" | |
phppid="$(netstat -ntpl | grep 32956 | sed 's/.*LISTEN *//' | sed 's#/.*##')" | |
[ ! -z "$phppid" ] && echo "After you have downloaded the file, stop the temporary web server with: kill $phppid" | tee -a "$(dirname $outputpath)/.buinstructions" | |
fi | |
echo "You can access your backup file via scp:" | tee -a "$(dirname $outputpath)/.buinstructions" | |
if [ "$encrypt" == "openssl" ] ; then | |
echo "mkdir restore.$dt && cd restore.$dt && scp root@$ip:$outputpath /dev/stdout | openssl enc -d -aes-256-cbc -md sha256 -pass "pass:$password" | tar --extract --gunzip --numeric-owner --preserve-permissions " | tee -a "$(dirname $outputpath)/.buinstructions" | |
echo "Should you need openssl for windows, you may download that. For example from https://curl.se/windows/" | tee -a "$(dirname $outputpath)/.buinstructions" | |
elif [ "$encrypt" == "none" ] ; then | |
echo "mkdir restore.$dt && cd restore.$dt && scp root@$ip:$outputpath /dev/stdout | tar --extract --gunzip --numeric-owner --preserve-permissions" | tee -a "$(dirname $outputpath)/.buinstructions" | |
elif [ "$encrypt" == "zip" ] ; then | |
echo "scp root@$ip:$outputpath ." | tee -a "$(dirname $outputpath)/.buinstructions" | |
else | |
echo "Unrecognized encrypt type of '$encrypt'" >&2 | |
exit 1 | |
fi | |
echo "The backup includes mysql databases. You may prefer to exclude them, and run a MySQL database dump instead." | tee -a "$(dirname $outputpath)/.buinstructions" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment