#!/bin/bash SELF="$0" HOST="" PORT="22" REMOTE_PATH="" NAME="" LOGIN="$(whoami)" die() { echo -e $1 | sed -e 's-^-! -' >&2 exit 1 } usage() { echo -e "usage: $SELF PROFILE_NAME --host=server --port=22 --login=backup-user --directory=/backup/path" >&2 exit 1 } generate_passphrase() { tr -dc A-Za-z0-9 "$CONFIGDIR/borg_passphrase" ssh-keygen -t ed25519 -N "" -q -f "$CONFIGDIR/ssh_key" -C "borg access from $(hostname --long)" ssh-copy-id -i "$CONFIGDIR/ssh_key.pub" -p $PORT "$LOGIN@$HOST" } set_env_config() { local file="$1" local param="$2" local value="$3" # test current config # file does not exist [ ! -e $file ] && echo "$param=$value" >> $file # file isn't readable [ ! -r $file ] && die "Config file isn't readable: $file" # file exists and value is set => early exit local curr_line="$(grep "^$param=" $file)" if [ "x$curr_line" == "x" ]; then # file ist nicht änderbar [ ! -w $file ] && die "Config file isn't writable: $file" # param isn't set => append echo "$param=$value" >> $file elif [ "x${curr_line#$param=}" != "x$value" ]; then # param is set with different value die "Environment parameter '$param' is already set to '${curr_line#$param=}' in config file: $file\nPlease confirm that you know what you're doing by manually setting the parameter to the desired value '$value'." fi return 0 } test_ssh() { ssh -q -o "BatchMode=yes" -i "$CONFIGDIR/ssh_key" -p "$PORT" "$LOGIN@$HOST" "mkdir -p $REMOTE_PATH" \ || die "SSH connection attempt failed." return 0 } test_repo_exists() { ssh -q -o "BatchMode=yes" -i "$CONFIGDIR/ssh_key" -p "$PORT" "$LOGIN@$HOST" "cat $REMOTE_PATH/README" 2>/dev/null | grep -q "This is a Borg Backup repository." return $? } invoke_borg() { # do some sandboxinng systemd-run --quiet --pipe --collect --unit=temp-borg-init-sandbox.service \ --working-directory=/tmp \ -p "ConfigurationDirectory=borg/$NAME" \ -p "CacheDirectory=borg/$NAME" \ -p "ConfigurationDirectoryMode=550" \ -p "CacheDirectoryMode=550" \ -p "PrivateTmp=yes" \ -p "ReadOnlyDirectories=/" \ -p "EnvironmentFile=/etc/borg/$NAME/config.env" \ --setenv=BORG_PASSPHRASE_FD=0 \ --setenv=BORG_BASE_DIR=/tmp/ \ --setenv=BORG_CONFIG_DIR=/etc/borg/$NAME \ --setenv=BORG_CACHE_DIR=/var/cache/borg/$NAME \ /usr/bin/borg $@ < /etc/borg/$NAME/borg_passphrase } init_repo() { echo "> init repo" invoke_borg init -e repokey-blake2 || die "failed to init borg repo" } show_repo() { echo "> testing repo connection" invoke_borg info || die "failed to access borg repo" } for arg in "$@"; do case "$arg" in -h*) HOST="${arg#-h}" ;; --host=*) HOST="${arg#--host=}" ;; -p*) PORT="${arg#-p}" ;; --port=*) PORT="${arg#--port=}" ;; -l*) LOGIN="${arg#-l}" ;; --login=*) LOGIN="${arg#--login=}" ;; -d*) REMOTE_PATH="${arg#-d}" ;; --directory=*) REMOTE_PATH="${arg#--directory=}" ;; -*) usage ;; *) [ "x$NAME" == "x" ] || usage NAME="$arg" ;; esac done [ "x$NAME" == "x" ] && usage [ "x$REMOTE_PATH" == "x" ] && usage CONFIGDIR="/etc/borg/$NAME" echo "> checking config" [ -e "$CONFIGDIR" ] || init_config_dir || die "Failed setting up config directory and ssh connection" [ -d "$CONFIGDIR" ] || die "Config directory isn't valid: $CONFIGDIR" [ -r "$CONFIGDIR/borg_passphrase" ] || die "File isn't readable: $CONFIGDIR/borg_passphrase" [ -r "$CONFIGDIR/ssh_key" ] || die "File isn't readable: $CONFIGDIR/ssh_key" # write config every time, parameter may change with each invocation set_env_config "$CONFIGDIR/config.env" BORG_REPO "ssh://$LOGIN@$HOST:${PORT}${REMOTE_PATH}" set_env_config "$CONFIGDIR/config.env" BORG_RSH "ssh -i $CONFIGDIR/ssh_key" [ -r "$CONFIGDIR/config.env" ] || die "File isn't readable: $CONFIGDIR/config.env" echo "> testing ssh and destination path" test_ssh || die "Can't establish ssh connection! Try: ssh-copy-id -i $CONFIGDIR/ssh_key.pub -p $PORT $LOGIN@$HOST" echo "> testing borg repo" test_repo_exists || init_repo show_repo