#!/usr/bin/env bash ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## restic-repos.sh ## ## ## ## Repository/user management script for ## ## Restic's REST server ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## # Information NAME="rest-repos.sh" DESCRIPTION="Repository/user management script for Restic's REST server" VERSION="0.1.0" AUTHOR="david@socialnerds.org" LICENSE="MIT" WEBSITE="https://git.socialnerds.org/david/scripts" # Configuration RR_DATA_PATH="/srv/backup/rest-server" RR_NGINX_PATH="/etc/nginx/rest-server.conf.d" RR_NGINX_TEMPLATE="$RR_NGINX_PATH/rest-server.conf.template" RR_LOG_FILE="/var/log/rest-repos.log" RR_HOST="backup.socialnerds.org" RR_PROTOCOL="https" # Initialization RR_DEPS="htpasswd tr head systemctl find awk sed sort" # Text formatting CLEAR="\e[0m" BOLD="\e[1m" RED="\e[31m" # Functions function rr_output() { # Print text/messages to screen if [[ "$1" =~ ^\! ]]; then printf "$RED%b$CLEAR\n" "${1#\!}" else if [[ "$RR_Q" -ne 1 ]]; then printf "%b\n" "$1" fi fi } function rr_help() { # Print help information rr_output "$DESCRIPTION\n\nUsage:\n$NAME \n\nOptions: -l, --list\t\tList repositories -r, --remove\t\tRemove repository -h, --help\t\tHelp screen -v, --version\t\tVersion information -q, --quiet\t\tNo output except errors" exit 0 } function rr_version() { # Print version information rr_output "Version: $BOLD$VERSION$CLEAR\nAuthor: $AUTHOR\nLicense: $LICENSE" exit 0 } function rr_passwd() { # Generate random password LC_ALL=C tr -dc 'a-zA-Z0-9' < /dev/urandom | head -c 16; echo } function rr_list() { # List all existing repositories for RR_REPO in $(find $RR_DATA_PATH -maxdepth 1 -mindepth 1 -type d -printf "%f\n" | sort); do local RR_SIZE=$(du -s -h $RR_DATA_PATH/$RR_REPO | awk '{print $1}') if [[ -f $RR_NGINX_PATH/$RR_REPO.conf && -f $RR_NGINX_PATH/$RR_REPO.htpasswd ]]; then rr_output "$BOLD$RR_REPO$CLEAR[$RR_SIZE]" else local RR_DISABLED="$RR_DISABLED $RR_REPO[$RR_SIZE]" fi done for RR_REPO in $RR_DISABLED; do rr_output "$RR_REPO" done } function rr_add() { # Enable or create a repository if [ -n "$1" ]; then if [[ ! -d "$RR_DATA_PATH/$1" ]]; then mkdir "$RR_DATA_PATH/$1" chown rest-server:rest-server "$RR_DATA_PATH/$1" chmod 700 "$RR_DATA_PATH/$1" fi if [[ ! -f "$RR_NGINX_PATH/$1.conf" && ! -f "$RR_NGINX_PATH/$1.htpasswd" ]]; then sed "s/RR_REPO/$1/g" $RR_NGINX_TEMPLATE > "$RR_NGINX_PATH/$1.conf" RR_PASSWD="$(rr_passwd)" htpasswd -n -b "$1" "$RR_PASSWD" > "$RR_NGINX_PATH/$1.htpasswd" rr_output "Repository has been added [$1]\nRepository URL:$BOLD rest:$RR_PROTOCOL://$1:$RR_PASSWD@$RR_HOST/$1/$CLEAR" else rr_output "!Repository does already exist and is enabled\nTry removing it first with -r or --remove" exit 1 fi fi } function rr_remove() { # Remove a repository if [[ -n "$1" ]]; then rm $RR_NGINX_PATH/$1.conf &>/dev/null rm $RR_NGINX_PATH/$1.htpasswd &>/dev/null if [[ -d $RR_DATA_PATH/$1 ]]; then rr_output "Repository has been removed [$1]\nThe data needs to be deleted manually [$RR_DATA_PATH/$1]" else rr_output "!Repository does not exist [$1]" exit 1 fi fi } # Preflight for RR_DEP in $RR_DEPS; do if ! $(command -v $RR_DEP &>/dev/null); then RR_MISS="$RR_MISS, $RR_DEP" fi done if [[ -n $RR_MISS ]]; then rr_output "!One or more missing dependencies [${RR_MISS#, }]\nTry installing them with your package manager" exit 1 fi #TODO: check if nginx path and data path exists and both are writeable # Option handler while [[ "$1" =~ ^- && ! "$1" == "--" ]]; do case $1 in -v|--version) RR_V=1 ;; -h|--help) RR_H=1 ;; -l|--list) RR_L=1 ;; -r|--remove) RR_R=1 ;; -q|--quiet) RR_Q=1 ;; *) rr_output "!Unknown option [$1]\nTry --help or -h for available options" exit 1 ;; esac shift done if [[ "$1" == '--' ]]; then shift fi if [[ $RR_L -eq 1 ]]; then rr_list exit 0 elif [[ $RR_H -eq 1 ]]; then rr_help exit 0 elif [[ $RR_V -eq 1 ]]; then rr_version exit 0 fi if [[ -z "$1" ]]; then rr_list exit 0 else if [[ "$1" =~ ^[a-zA-Z0-9_-]{3,20}$ ]]; then if [[ $RR_R -eq 1 ]]; then rr_remove "$1" else rr_add "$1" fi # Reloading Nginx after config changes systemctl reload nginx.service else rr_output "!Invalid repository name [$1]\nMust be an alphanumeric string between 3 and 20 characters" exit 1 fi fi # Here be dragons exit 0