diff --git a/consul b/consul new file mode 100755 index 0000000..b64bd49 --- /dev/null +++ b/consul @@ -0,0 +1,66 @@ +#!/bin/bash + +restoreDir=/etc/restore +consulDir="/var/backups/consul" +scriptDir="$(dirname "$0")" + +readConfig() { + if [[ -r "${scriptDir}/../config/consul_backup.cfg" ]]; then + source "${scriptDir}/../config/consul_backup.cfg" + fi +} + +check() { + if [[ -d "$consulDir" ]]; then + echo "Cleaning out old Consul backups..." + rm -f "$consulDir"/* || return 1 + else + mkdir -p "$consulDir" | return 1 + fi + + if [[ -n "$CONSUL_PATH" ]]; then + if [[ -x "${CONSUL_PATH}/consul" ]]; then + echo "Found Consul in $CONSUL_PATH" + else + echo "FATAL: Cannot execute Consul in $CONSUL_PATH" + return 3 + fi + else + if ! which consul &>/dev/null; then + echo "Cannot find Consul in PATH" + return 2 + fi + if [[ -x "$(which consul)" ]]; then + CONSUL_PATH="$(dirname "$(which consul)")" + echo "Found Consul in $CONSUL_PATH" + else + echo "FATAL: Cannot execute Consul" + return 3 + fi + fi +} + +hook_before() { + readConfig + check || exit $? + ${CONSUL_PATH}/consul snapshot save "$consulDir/consul.snap" +} + +hook_after() { + : +} + +hook_fail() { + : +} + +hook_final() { + : +} + +case "$1" in + before) hook_before || exit $?;; + after) hook_after || exit $?;; + fail) hook_fail || exit $?;; + finally) hook_final || exit $?;; +esac diff --git a/flatpak b/flatpak new file mode 100755 index 0000000..b790392 --- /dev/null +++ b/flatpak @@ -0,0 +1,80 @@ +#!/bin/bash + +restoreDir="/etc/restore" + +hook_before() { + mkdir -p "$restoreDir" || exit 1 + pushd "$restoreDir" || exit 2 + + which flatpak || exit 0 + + flatpak list --columns=application --app > flatpaks.lst + + cat > restore-flatpak.sh </dev/null; then + echo "Cannot find Gitea in PATH" + return 2 + fi + if [[ -x "$(which gitea)" ]]; then + GITEA_BIN="$(which gitea)" + echo "Found Gitea in $GITEA_BIN" + else + echo "FATAL: Cannot execute Gitea" + return 3 + fi + fi + + if [[ -d "$giteaDir" ]]; then + echo "Cleaning out old Gitea backups..." + rm -f "$giteaDir"/* || return 1 + else + mkdir -p "$giteaDir" | return 1 + chgrp "$GITEA_GROUP" "$giteaDir" + chmod g+wx "$giteaDir" + fi +} + +runBackups() { + local backupDate + + backupDate=$(date +"%Y-%m-%d") + giteaHome=$(getent passwd git | cut -f6 -d:) + pushd "${giteaDir}" &>/dev/null + sudo -u "$GITEA_USER" "${GITEA_BIN}" dump --config "${GITEA_CONFIG}" --type tar.xz --file - > "${giteaDir}/gitea-dump-${backupDate}.tar.xz" + popd &>/dev/null +} + +hook_before() { + check || exit $? + runBackups || exit $? +} + +hook_after() { + : +} + +hook_fail() { + : +} + +hook_final() { + : +} + + +case "$1" in + before) hook_before || exit $?;; + after) hook_after || exit $?;; + fail) hook_fail || exit $?;; + finally) hook_final || exit $?;; +esac diff --git a/mysql_backup b/mysql_backup new file mode 100755 index 0000000..74074ed --- /dev/null +++ b/mysql_backup @@ -0,0 +1,58 @@ +#!/bin/bash + +restoreDir=/etc/restore +mysqlDir="$restoreDir/mysql" +scriptDir="$(dirname "$0")" + +function readConfig() { + if [[ -r "${scriptDir}/../config/mysql_backup.cfg" ]]; then + source "${scriptDir}/../config/mysql_backup.cfg" + fi +} + +function runBackups() { + local dbname + + readConfig + + if [[ ! -d "/var/backups/mysql" ]]; then + mkdir -p "/var/backups/mysql" || return 1 + fi + echo "Clearing out old MySQL backups..." + rm -f /var/backups/mysql/* + + while read dbname + do + case "$dbname" in + sys) continue;; + information_schema) continue;; + performance_schema) continue;; + esac + + echo "Backing up database: $dbname" + mysqldump --complete-insert --routines --triggers --single-transaction "$dbname" > /var/backups/mysql/"$dbname".sql + done < <(mysql -N -e 'show databases') +} + +hook_before() { + runBackups || exit $? +} + +hook_after() { + rm -rf "$mysqlDir" || exit 1 +} + +hook_fail() { + : +} + +hook_final() { + : +} + +case "$1" in + before) hook_before || exit $? ;; + after) hook_after || exit $? ;; + fail) hook_fail || exit $? ;; + finally) hook_final || exit $? ;; +esac diff --git a/openldap b/openldap new file mode 100755 index 0000000..a1618c5 --- /dev/null +++ b/openldap @@ -0,0 +1,74 @@ +#!/bin/bash + +restoreDir=/etc/restore +ldapDir="$restoreDir/openldap" + +hook_before() { + if [[ -d "$ldapDir" ]]; then + rm -rf "$ldapDir" || exit 1 + fi + mkdir -p "$ldapDir" || exit 1 + pushd "$restoreDir" || exit 2 + + slapcat -n 0 -l "$ldapDir/config.ldif" + slapcat -n 1 -l "$ldapDir/data.ldif" + + cat > ldaprestore.sh </dev/null; then + "${scriptPath}/flatpak" "$hook" + fi + fi + return $? +} + +hook_before() { + checkOS || exit 200 + pushd "$scriptPath" &>/dev/null || exit 201 + git checkout -- . + git pull + popd &>/dev/null || exit 201 + + createRestoreDir || exit $? + runOsHook before +} + +hook_fail() { + checkOS || exit 200 + runOsHook fail +} + +hook_after() { + checkOS || exit 200 + runOsHook after +} + +hook_final() { + checkOS || exit 200 + if [[ ! -f "${restoreDir}/.do-not-delete" ]]; then + rm -rf "$restoreDir" + fi + runOsHook final +} + + +case "$1" in + before) hook_before || exit $?;; + after) hook_after || exit $?;; + fail) hook_final || exit $?;; + finally) hook_final || exit $?;; +esac diff --git a/os_arch b/os_arch new file mode 100755 index 0000000..5fd8b54 --- /dev/null +++ b/os_arch @@ -0,0 +1,46 @@ +#!/bin/bash + +restoreDir="/etc/restore" + +hook_before() { + mkdir -p "$restoreDir" || exit 1 + pushd "$restoreDir" || exit 2 + + pacman -Qqe > "pkglist.txt" + comm -13 <(pacman -Qqdt | sort) <(pacman -Qqdtt | sort) > optdeplist.txt + pacman -Qqem > foreignpkglist.txt + + cat > restore.sh </dev/null +} + +hook_before() { + if ! is_bin_in_path rsync; then + echo "rsync needs to be installed for backups to work properly." + exit 1 + fi + + mkdir -p "$restoreDir" || exit 1 + pushd "$restoreDir" || exit 2 + + dpkg --get-selections | sort > Package.list + apt-mark showmanual | sort > InstallOnly.list + cp -a /etc/apt/sources.list "$restoreDir/" + rsync -avhHi /etc/apt/sources.list.d "$restoreDir/" + rsync -avhHi /etc/apt/trusted.gpg.d "$restoreDir/" + [[ -d /etc/apt/keyrings ]] && rsync -avhHi /etc/apt/keyrings "$restoreDir/" + + cat > restore.sh </dev/null || install+=" rsync" +#dpkg-query -s 'borgbackup' &>/dev/null || install+=" borgbackup" +#dpkg-query -s 'borgmatic' &>/dev/null || install+=" borgmatic" +dpkg-query -s 'apt-transport-https' &>/dev/null || install+=" apt-transport-https" + +if [[ -n "\$install" ]]; then + apt -y install \$install +fi + +echo "\${bold} * Enabling 32-bit packages\${normal}" +grep ':i386' InstallOnly.list &>/dev/null && dpkg --add-architecture i386 + +echo "\${bold} * Checking for flatpak\${normal}" +flatpak=0 +grep 'flatpak' InstallOnly.list &>/dev/null && flatpak=1 +if [[ "\$flatpak" -eq 1 ]]; then + echo " * Adding flatpak repo: Flathub" + flatpak remote-add --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo +fi + +if [[ -r "sources.list" ]]; then + echo "\${bold}" + echo "==============================" + echo "INSTALL REPOS" + echo "==============================" + echo "\${normal}" + + read -p "\${bold}Do you want to continue to install backup repositories? [Y/n]\${normal} " -n 1 -sr promptRepos + if [[ "\$promptRepos" =~ ^[Yy]$ ]]; then + echo "Yes" + cp -a sources.list /etc/apt/sources.list + rsync --ignore-existing -raz sources.list.d/ /etc/apt/sources.list.d/ + rsync --ignore-existing -raz trusted.gpg.d/ /etc/apt/trusted.gpg.d/ + [[ -d keyrings ]] && rsync --ignore-existing -raz keyrings/ /etc/apt/keyrings/ + apt update + else + echo -e "Cancelled\n" + fi +fi + +echo "\${bold}" +echo "==============================" +echo "INSTALL PACKAGES" +echo "==============================" +echo "\${normal}" + +read -p "\${bold}About to install the system packages per the backup. Do you want to continue? [Y/n]\${normal} " -n 1 -sr promptPkgs +if [[ "\$promptPkgs" =~ ^[Yy] ]]; then + echo "Yes" + comm --nocheck-order -23 InstallOnly.list <(apt-mark showmanual|sort) | grep -Ev 'linux-image|linux-headers' > "\${TMPDIR}/diff.list" + apt-get --simulate install \$(cat "\${TMPDIR}/diff.list") |& awk '/^E: Unable to locate package / {print \$NF}' | sort > "\${TMPDIR}/diff.fail" + comm --nocheck-order -23 "\${TMPDIR}/diff.list" "\${TMPDIR}/diff.fail" | xargs apt-get install + + echo + echo "Packages that were omitted because they could not be found:" + cat "\${TMPDIR}/diff.fail" | tr '\n' ' ' | fold -s + echo + echo + + read -p "\${bold}Did everything above look okay and do you want to proceed? [Y/n]\${normal} " -n 1 -sr promptPkgsDo + if [[ "\$promptPkgsDo" =~ ^[Yy] ]]; then + comm --nocheck-order -23 "\${TMPDIR}/diff.list" "\${TMPDIR}/diff.fail" | xargs apt-get install + + echo + echo + echo "\${bold}Packages that failed to schedule for install:\${normal}" + cat "\${TMPDIR}/diff.fail" | tr '\n' ' ' | fold -s + echo + echo + else + echo -e "Cancelled\n" + fi +else + echo -e "Cancelled\n" +fi +EOF + chmod ug+rx restore.sh + popd || exit 2 +} + +hook_after() { + : +} + +hook_fail() { + : +} + +hook_final() { + : +} + +case "$1" in + before) hook_before || exit $?;; + after) hook_after || exit $?;; + fail) hook_final || exit $?;; + finally) hook_final || exit $?;; +esac diff --git a/os_fedora b/os_fedora new file mode 100755 index 0000000..095dd0e --- /dev/null +++ b/os_fedora @@ -0,0 +1,45 @@ +#!/bin/bash + +restoreDir="/etc/restore" + +hook_before() { + mkdir -p "$restoreDir" || exit 1 + pushd "$restoreDir" || exit 2 + + rpm -qa | sort > Package.versions.list + rpm -qa --queryformat '%{NAME}.%{ARCH}\n' | sort > Package.list + #FIXME Need to put this to use for explicitely installed packages. + dnf repoquery --userinstalled > Package.userinstalled.list + + cat > restore.sh < restore.sh < Packages.list + + popd || exit 2 +} + +hook_after() { + : +} + +hook_fail() { + : +} + +hook_final() { + : +} + +case "$1" in + before) hook_before || exit $?;; + after) hook_after || exit $?;; + fail) hook_fail || exit $?;; + finally) hook_final || exit $?;; +esac diff --git a/os_suse b/os_suse new file mode 100755 index 0000000..8a51b3c --- /dev/null +++ b/os_suse @@ -0,0 +1,172 @@ +#!/bin/bash + +restoreDir="/etc/restore" + +hook_before() { + mkdir -p "$restoreDir" || exit 1 + pushd "$restoreDir" || exit 2 + + rpm -qa | sort > Package.versions.list + rpm -qa --queryformat '%{NAME}.%{ARCH}\n' | sort > Package.all.list + zypper se -i -t pattern | awk '/^i\+/ { print $3 }' | sort > Pattern.list + zypper se -i -t package | awk '/^i\+/ { print $3 }' | sort > Package.list + zypper lr -e Backup.repos + zypper ll | awk '/.*\| package.*/ { print $3 }' > Package.lock + + cat > restore.sh < /tmp/Package.new.list + echo "Here's a list of packages that would be removed:" + comm -13 Package.all.list /tmp/Package.new.list + + read -p "Do you want to omit any removals or cancel? [N, y, c] " -n 1 -sr promptRmVerify + if [[ "\$promptRmVerify" =~ ^[Yy]$ ]]; then + echo + read -p "What do you want to omit, seperated by spaces? " -r promptOmit + omitrm="\$(echo "\$promptOmit" | tr ' ' '|')" + echo + elif [[ "\$promptRmVerify" =~ ^[Cc]$ ]]; then + echo -e "Cancelled\n" + omitrm="CANCEL" + else + omitrm="" + echo + fi + + if [[ "\$omitrm" != "CANCEL" ]]; then + comm -13 Package.all.list /tmp/Package.new.list | egrep -v "(\$omitrm)" | xargs zypper remove + fi +else + echo -e "Cancelled\n" +fi + +# Package Locks +if [[ -r "Package.lock" && "\$(wc -l --total=only Package.list)" -gt 0 ]]; then + echo + echo "==============================" + echo "PACKAGE LOCKS" + echo "==============================" + echo + echo "The following packages are found to be locked:" + cat Package.lock + echo + read -p "Do you want to lock these packages per the backup? [Y/n] " -n 1 -sr promptLock + if [[ "\$promptLock" =~ ^[Yy]$ ]]; then + echo "Yes" + cat Package.lock | xargs zypper addlock + else + echo -e "Cancelled\n" + fi +fi +EOF + + popd || exit 2 +} + +hook_after() { + : +} + +hook_fail() { + : +} + +hook_final() { + : +} + +case "$1" in + before) hook_before || exit $?;; + after) hook_after || exit $?;; + fail) hook_fail || exit $?;; + finally) hook_final || exit $?;; +esac diff --git a/postgresql b/postgresql new file mode 100755 index 0000000..86e3e91 --- /dev/null +++ b/postgresql @@ -0,0 +1,70 @@ +#!/bin/bash + +restoreDir=/etc/restore +pgsqlDir="$restoreDir/postgresql" +scriptDir="$(dirname "$0")" + +function readConfig() { + local $config + + if [[ -r "${scriptDir}/../config/postgresql.cfg" ]]; then + config="$(readlink -f "${scriptDir}/../config/postgresql.cfg")" + else + exit 2 + fi + + while read -r -a vals + do + [[ "${vals[*]}" =~ ^#.*$ ]] && continue + [[ -z "${vals[0]}" ]] && continue + if [[ "${vals[0]}" -ge 10 ]]; then + echo "${vals[@]}" + fi + done < "$config" +} + +function runBackups() { + local vals + local version + local cluster + + while read -r -a vals + do + version="${vals[0]}" + cluster="${vals[1]}" + + echo "Backing up PostgreSQL $version - $cluster" + pg_backupcluster "$version" "$cluster" createdirectory || return $? + sudo -u postgres pg_backupcluster "$version" "$cluster" basebackup || return $? + sudo -u postgres pg_backupcluster "$version" "$cluster" dump || return $? + sudo -u postgres pg_backupcluster "$version" "$cluster" expirebasebackups 1 || return $? + sudo -u postgres pg_backupcluster "$version" "$cluster" expiredumps 1 || return $? + done < <(readConfig) +} + +hook_before() { + if [[ -d "$pgsqlDir" ]]; then + rm -rf "$pgsqlDir" || exit 1 + fi + + runBackups || exit $? +} + +hook_after() { + rm -rf "$pgsqlDir" || exit 1 +} + +hook_fail() { + : +} + +hook_final() { + : +} + +case "$1" in +before) hook_before || exit $? ;; +after) hook_after || exit $? ;; +fail) hook_fail || exit $? ;; +finally) hook_final || exit $? ;; +esac diff --git a/run-parts b/run-parts new file mode 100755 index 0000000..c164b9d --- /dev/null +++ b/run-parts @@ -0,0 +1,101 @@ +#!/bin/bash +# run-parts - concept taken from Debian + +# keep going when something fails +set +e + +if [ $# -lt 1 ]; then + echo "Usage: run-parts [--args | --list | --test] " + exit 1 +fi + +while [ $# -gt 1 ]; do + case $1 in + --args) + runargs=$2 + shift 2 + break + ;; + --list) + list=1 + shift + break + ;; + --test) + test=1 + shift + break + ;; + --) + # -- end of options + shift + break + ;; + *) + # directory + break + ;; + esac +done + +if [ ! -d $1 ]; then + echo "Not a directory: $1" + exit 1 +fi + +if [ -f /etc/sysconfig/run-parts ]; then + . /etc/sysconfig/run-parts +fi + +# Ignore *~ and *, scripts +for i in $(LC_ALL=C; echo ${1%/}/*[^~,]) ; do + [ -d $i ] && continue + # Don't run *.{rpmsave,rpmorig,rpmnew,swp,cfsaved} scripts + [ "${i%.cfsaved}" != "${i}" ] && continue + [ "${i%.rpmsave}" != "${i}" ] && continue + [ "${i%.rpmorig}" != "${i}" ] && continue + [ "${i%.rpmnew}" != "${i}" ] && continue + [ "${i%.swp}" != "${i}" ] && continue + [ "${i%,v}" != "${i}" ] && continue + + # jobs.deny prevents specific files from being executed + # jobs.allow prohibits all non-named jobs from being run. + # can be used in conjunction but there's no reason to do so. + if [ -r $1/jobs.deny ]; then + grep -q "^$(basename $i)$" $1/jobs.deny && continue + fi + if [ -r $1/jobs.allow ]; then + grep -q "^$(basename $i)$" $1/jobs.allow || continue + fi + + if [ -e $i ]; then + if [ -r $1/whitelist ]; then + grep -q "^$(basename $i)$" $1/whitelist && continue + fi + + if [ ${list:-0} = 1 ]; then + echo $i; + elif [ -x $i ]; then + if [ ${test:-0} = 1 ]; then + echo $i; + continue + fi + if [ "$RANDOMIZE" != "" ]; then + let "rtime = $RANDOM" + if [ "$RANDOMTIME" != "" ]; then + let "rtime %= $RANDOMTIME" + else + let "rtime %= 300" + fi + sleep $rtime + fi + + # run executable files + echo "run-parts[$$]" "($1) starting $(basename $i)" + $i $runargs 2>&1 + echo "run-parts[$$]" "($1) finished $(basename $i)" + fi + fi +done + +exit 0 diff --git a/vaultwarden b/vaultwarden new file mode 100755 index 0000000..5986c56 --- /dev/null +++ b/vaultwarden @@ -0,0 +1,36 @@ +#!/bin/bash + +restoreDir=/etc/restore +vaultwardenDir="$restoreDir/vaultwarden" + +hook_before() { + if [[ -d "$vaultwardenDir" ]]; then + rm -rf "$vaultwardenDir" || exit 1 + fi + + test -x "$(which sqlite3)" || exit 2 + backupDate=$(date +"%Y-%m-%d") + vaultwardenHome=$(getent passwd vaultwarden | cut -f6 -d:) + + mkdir -p "$vaultwardenDir" + sqlite3 "${vaultwardenHome}/data/db.sqlite3" ".backup '${vaultwardenDir}/db-${backupDate}.sqlite3'" +} + +hook_after() { + rm -rf "$vaultwardenDir" || exit 1 +} + +hook_fail() { + : +} + +hook_final() { + : +} + +case "$1" in + before) hook_before || exit $?;; + after) hook_after || exit $?;; + fail) hook_fail || exit $?;; + finally) hook_final || exit $?;; +esac