#!/usr/bin/bash # backupd - v1.0.0 # a simple backup script # # Copyright (c) 2025 Tianyi CodeLab, under the MIT License. DATA_DIR="$(cd "$(dirname "$0")" && pwd)" RCLONE_DEST_NAME="dest" ZSTD_LEVEL=13 main() { local backup_name backup_remote backup_name="backups_$(hostname)_$(date +%Y-%m-%d)_$(date +%H:%M:%S)" backup_remote="$RCLONE_DEST_NAME:server-$(hostname)" check_files # shellcheck disable=SC1091 . "$DATA_DIR/passwd" if [ -z "$AES_PASSWD" ]; then echo -ne "\033[1;31mAES_PASSWD is not set in file 'passwd'. Using default hostname as password.\033[0m\n" echo -ne "\033[1;31mWarning: THIS IS NOT SECURE!\033[0m\n" AES_PASSWD="$(hostname)" fi if [ -z "$ITER_COUNT" ]; then echo -ne "\033[1;31mITER_COUNT is not set in file 'passwd'. Using default value 100000.\033[0m\n" ITER_COUNT=100000 fi local ctx local tar_file zst_file enc_file ext_list inc_list local tar_hash zst_hash enc_hash ctx="$(mktemp -d)" tar_file="$ctx/$backup_name.tar" zst_file="$ctx/$backup_name.tar.zst" enc_file="$ctx/$backup_name.tar.zst.enc" ext_list="$DATA_DIR/excludes" inc_list="$DATA_DIR/includes" echo -ne "\033[1;33mCreating backup $backup_name\033[0m\n" exec_hooks pre echo -ne "\033[1;33mCreating archive $tar_file\033[0m\n" tar -cvf "$tar_file" -X "$ext_list" -T "$inc_list" echo -ne "\033[1;33mCompressing archive $zst_file\033[0m\n" zstd -T -${ZSTD_LEVEL} "$tar_file" -o "$zst_file" echo -ne "\033[1;33mEncrypting archive $enc_file\033[0m\n" openssl enc -aes-256-cbc -salt -pbkdf2 -iter "$ITER_COUNT" -in "$zst_file" -out "$enc_file" -k "$AES_PASSWD" tar_hash="$(sha256sum "$tar_file" | cut -d ' ' -f 1)" zst_hash="$(sha256sum "$zst_file" | cut -d ' ' -f 1)" enc_hash="$(sha256sum "$enc_file" | cut -d ' ' -f 1)" echo -ne "\033[1;34mArchive hash: $tar_hash\033[0m\n" echo -ne "\033[1;34mCompressed hash: $zst_hash\033[0m\n" echo -ne "\033[1;34mEncrypted hash: $enc_hash\033[0m\n" echo -ne "\033[1;33mUploading archive to remote...\033[0m\n" echo -ne "\033[1;34mRemote filename: $backup_remote\033[0m\n" RCLONE_CONFIG="$DATA_DIR/rclone.conf" rclone copy \ -vv --checksum --no-traverse --s3-no-check-bucket --metadata \ --metadata-set "sha256-enc=$enc_hash" \ --metadata-set "sha256-zst=$zst_hash" \ --metadata-set "sha256-tar=$tar_hash" \ -P "$enc_file" "$backup_remote" exec_hooks post rm -rvf "$ctx" echo -ne "\033[1;34m------------ backup done ------------\033[0m\n" } check_files() { if [ ! -f "$DATA_DIR/includes" ]; then echo -ne "\033[1;31mCreating configuration file 'includes'...\033[0m\n" touch "$DATA_DIR/includes" fi if [ ! -f "$DATA_DIR/excludes" ]; then echo -ne "\033[1;31mCreating configuration file 'excludes'...\033[0m\n" touch "$DATA_DIR/excludes" fi if [ ! -d "$DATA_DIR/hooks-pre.d" ]; then echo -ne "\033[1;31mCreating directory 'hooks-pre.d'...\033[0m\n" mkdir -p "$DATA_DIR/hooks-pre.d" fi if [ ! -d "$DATA_DIR/hooks-post.d" ]; then echo -ne "\033[1;31mCreating directory 'hooks-post.d'...\033[0m\n" mkdir -p "$DATA_DIR/hooks-post.d" fi } exec_hooks() { local hook_type="$1" echo -ne "\033[1;32mRunning $hook_type hooks...\033[0m\n" for hook in "$DATA_DIR/hooks-$hook_type.d"/*; do if [ -x "$hook" ]; then echo -ne "------------ Running hook: $hook\n" "$hook" fi done } main "$@"