#!/bin/bash # info: add backup host # options: TYPE HOST USERNAME PASSWORD [PATH] [PORT] # # example: v-add-backup-host sftp backup.acme.com admin p4$$w@Rd # v-add-backup-host b2 bucketName keyID applicationKey # # Add a new remote backup location. Currently SFTP, FTP and Backblaze are supported #----------------------------------------------------------# # Variables & Functions # #----------------------------------------------------------# # Argument definition type=$1 host=$2 user=$3 raw_password=$4 HIDE=4 password=$(perl -e 'print quotemeta shift(@ARGV)' "${raw_password}") path=${5-/backup} port=$6 # CPU Architecture arch=$(arch) # Includes # shellcheck source=/usr/local/hestia/func/main.sh source $HESTIA/func/main.sh # load config file source_conf "$HESTIA/conf/hestia.conf" # Fetch current verison B2 CLI tool source_conf "$HESTIA/install/upgrade/upgrade.conf" # Paths b2cli="/usr/local/bin/b2" b2lnk="https://github.com/Backblaze/B2_Command_Line_Tool/releases/download/v$b2_v/b2-linux" # Defining ftp command function ftpc() { ftp -p -n $host $port << EOF quote USER $user quote PASS $password binary $1 $2 $3 quit EOF } # Defining sftp command function sftpc() { if [ "$privatekey" != "yes" ]; then expect -f "-" "$@" << EOF set count 0 spawn /usr/bin/sftp -o StrictHostKeyChecking=no -o Port=$port $user@$host expect { -nocase "password:" { send "$password\r" exp_continue } -re "Password for (.*)@(.*)" { send "$password\r" exp_continue } -re "Couldn't|(.*)disconnect|(.*)stalled|(.*)not found" { set count \$argc set output "Disconnected." set rc $E_FTP exp_continue } -re ".*denied.*(publickey|password)." { set output "Permission denied, wrong publickey or password." set rc $E_CONNECT } "sftp>" { if {\$count < \$argc} { set arg [lindex \$argv \$count] send "\$arg\r" incr count } else { send "exit\r" set output "Disconnected." if {[info exists rc] != 1} { set rc $OK } } exp_continue } timeout { set output "Connection timeout." set rc $E_CONNECT } } if {[info exists output] == 1} { puts "\$output" } exit \$rc EOF else expect -f "-" "$@" << EOF set count 0 spawn /usr/bin/sftp -o StrictHostKeyChecking=no -o Port=$port -i $raw_password $user@$host expect { -re "Couldn't|(.*)disconnect|(.*)stalled|(.*)not found" { set count \$argc set output "Disconnected." set rc $E_FTP exp_continue } -re ".*denied.*(publickey|password)." { set output "Permission denied, wrong publickey or password." set rc $E_CONNECT } "sftp>" { if {\$count < \$argc} { set arg [lindex \$argv \$count] send "\$arg\r" incr count } else { send "exit\r" set output "Disconnected." if {[info exists rc] != 1} { set rc $OK } } exp_continue } timeout { set output "Connection timeout." set rc $E_CONNECT } } if {[info exists output] == 1} { puts "\$output" } exit \$rc EOF fi } #----------------------------------------------------------# # Verifications # #----------------------------------------------------------# if [ "$type" != 'local' ]; then check_args '2' "$#" "TYPE HOST USERNAME PASSWORD [PATH] [PORT]" is_format_valid 'host' 'path' 'port' is_type_valid 'sftp,ftp,b2,rclone' "$type" is_username_format_valid "$user" "username" privatekey="no" if [ -f "$raw_password" ]; then if [[ $(cat "$raw_password" | grep "OPENSSH PRIVATE") ]]; then privatekey="yes" password="$raw_password" else is_password_valid fi else is_password_valid fi format_no_quotes "$password" "password" if [ "$type" = 'sftp' ]; then which expect > /dev/null 2>&1 check_result $? "expect command not found" "$E_NOTEXIST" fi if [ "$type" != 'b2' ] && [ "$type" != 'rclone' ]; then if ! (is_ip_format_valid "$host" > /dev/null); then host "$host" > /dev/null 2>&1 check_result $? "host connection failed" "$E_CONNECT" fi fi fi # Perform verification if read-only mode is enabled check_hestia_demo_mode #----------------------------------------------------------# # Action # #----------------------------------------------------------# # Checking network connection if [ "$type" = 'ftp' ]; then if [ -z $port ]; then port=21 fi fconn=$(ftpc 2>&1) ferror=$(echo $fconn \ | grep -i -e failed -e error -e "can't" -e "not conn" -e "incorrect") if [ -n "$ferror" ]; then echo "Error: can't login to ftp $user@$host" log_event "$E_CONNECT" "$ARGUMENTS" exit "$E_CONNECT" fi # Checking write permissions if [ -z $path ]; then ftmpdir="vst.bK76A9SUkt" else ftpc "mkdir $path" > /dev/null 2>&1 ftmpdir="$path/vst.bK76A9SUkt" fi ftp_result=$(ftpc "mkdir $ftmpdir" "rm $ftmpdir" | grep -v Trying) if [ -n "$ftp_result" ]; then echo "$ftp_result" rm -rf $tmpdir echo "Error: can't create $ftmpdir folder on the ftp" log_event "$E_FTP" "$ARGUMENTS" exit "$E_FTP" fi fi if [ "$type" = 'sftp' ]; then if [ -z $port ]; then port=22 fi if [ -z $path ]; then sftmpdir="vst.bK76A9SUkt" sftpc "mkdir $sftmpdir" "rmdir $sftmpdir" > /dev/null 2>&1 else if sftpc "mkdir $path" > /dev/null 2>&1; then sftmpdir="$path/vst.bK76A9SUkt" sftpc "mkdir $sftmpdir" "rmdir $sftmpdir" > /dev/null 2>&1 else sftmpdir="$path/vst.bK76A9SUkt" sftpc "mkdir $sftmpdir" "rmdir $sftmpdir" > /dev/null 2>&1 fi fi rc=$? if [[ "$rc" != 0 ]]; then case $rc in $E_CONNECT) echo "Error: can't login to sftp $user@$host" ;; $E_FTP) echo "Error: can't create temp folder on the sftp host" ;; esac log_event "$rc" "$ARGUMENTS" exit "$rc" fi fi if [ "$type" = 'b2' ]; then # Download b2 binary if [ ! -f "$b2cli" ]; then if [ "$arch" = 'aarch64' ] || [ "$arch" = 'arm64' ]; then echo "Error: B2 binary for arm64 must be downloaded manually." exit 3 else wget -O $b2cli $b2lnk > /dev/null 2>&1 chmod +x $b2cli > /dev/null 2>&1 fi if [ ! -f "$b2cli" ]; then echo "Error: Binary download failed, b2 doesn't work as expected." exit 3 fi fi # Validate b2 binary b2version="$(b2 version)" if [[ ! "$b2version" =~ "b2 command line tool" ]]; then echo "Error: Binary download failed, b2 doesn't work as expected." exit 3 fi b2 clear-account > /dev/null 2>&1 b2 authorize-account "$user" "$raw_password" > /dev/null 2>&1 b2 ls --long "$host" "$user" > /dev/null 2>&1 if [ $? -ne 0 ]; then check_result "$E_CONNECT" "b2 failed to verify connection" fi fi if [ "$type" = 'rclone' ]; then curl -s https://rclone.org/install.sh | bash /dev/null > /dev/null 2>&1 # Verify account exists if [ ! -z "$(cat /root/.config/rclone/rclone.conf | grep "\[$host\]")" ]; then echo "test" > /tmp/hestia-backup.txt # Try to upload a single file if [ -z "$path" ]; then rclone copy /tmp/hestia-backup.txt $host:/hestia-backup.txt rclone delete $host:/hestia-backup.txt else rclone copy /tmp/hestia-backup.txt $host:$path/hestia-backup.txt rclone delete $host:$path/hestia-backup.txt fi else check_result "$E_CONNECT" "Rclone config does not exist" fi fi # Adding backup host if [ $type == 'ftp' ] || [ $type = 'sftp' ]; then new_timestamp str="HOST='$host'\nUSERNAME='$user'\nPASSWORD='$password'\nPRIVATEKEY='$privatekey'" str="$str\nBPATH='$path'\nPORT='$port'\nTIME='$time'\nDATE='$date'" echo -e "$str" > $HESTIA/conf/$type.backup.conf chmod 660 $HESTIA/conf/$type.backup.conf elif [ $type == 'b2' ]; then new_timestamp str="BUCKET='$host'\nB2_KEYID='$user'\nB2_KEY='$raw_password'" str="$str\nTIME='$time'\nDATE='$date'" echo -e "$str" > $HESTIA/conf/$type.backup.conf chmod 660 $HESTIA/conf/$type.backup.conf elif [ $type == "rclone" ]; then new_timestamp str="HOST='$host'\nBPATH='$path'" str="$str\nTIME='$time'\nDATE='$date'" echo -e "$str" > $HESTIA/conf/$type.backup.conf fi #----------------------------------------------------------# # Hestia # #----------------------------------------------------------# # Update hestia.conf if [ -z "$(grep BACKUP_SYSTEM $HESTIA/conf/hestia.conf)" ]; then echo "BACKUP_SYSTEM='$type'" >> $HESTIA/conf/hestia.conf else bckp=$(echo "$BACKUP_SYSTEM,$type" \ | sed "s/,/\n/g" \ | sort -r -u \ | sed "/^$/d" \ | sed ':a;N;$!ba;s/\n/,/g') sed -i "s/BACKUP_SYSTEM=.*/BACKUP_SYSTEM='$bckp'/g" $HESTIA/conf/hestia.conf fi # Logging log_event "$OK" "$ARGUMENTS" exit