1
0
forked from finn/tinyboard
Files
tinyboard/hubspoke-helper.sh
2026-04-13 12:31:45 -07:00

200 lines
5.9 KiB
Bash
Executable File

#!/usr/bin/env bash
#
# hubspoke-helper.sh - Manage hub/spoke rclone mounts over reverse SSH
#
# This script helps configure and control the two sides:
# - Spoke: establishes a reverse SSH tunnel (autossh)
# - Hub: mounts the spoke's filesystem via rclone/sftp
#
# It expects service template files in:
# ./spoke/autossh-tunnel.service
# ./hub/rclone-mount@.service
#
# Those files can be copied or piped into place by this script.
set -euo pipefail
# ------------------------------------------------------------
# Configuration (adjust these to your environment)
# ------------------------------------------------------------
SPOKE_AUTOSSH_SERVICE_SRC="./spoke/autossh-tunnel.service"
HUB_RCLONE_SERVICE_SRC="./hub/rclone-mount@.service"
# Default values (can be overridden with environment variables or interactive prompts)
HUB_USER="${HUB_USER:-armbian}"
HUB_HOST="${HUB_HOST:-oily.dad}"
SPOKE_USER="${SPOKE_USER:-armbian}"
TUNNEL_PORT="${TUNNEL_PORT:-11111}"
SPOKE_SSH_KEY="${SPOKE_SSH_KEY:-$HOME/.ssh/armbian-brie-202604}"
RCLONE_REMOTE_NAME="${RCLONE_REMOTE_NAME:-brie-remote}"
MOUNT_POINT="${MOUNT_POINT:-$HOME/mnt/brie}"
# ------------------------------------------------------------
# Helper functions
# ------------------------------------------------------------
usage() {
cat <<EOF
Usage: $0 {hub|spoke} {action}
Manage hub/spoke rclone setup.
ACTIONS FOR SPOKE:
show-cmd Show the autossh command to run manually
install Install the autossh system service (requires sudo)
start Start the installed service (requires sudo)
stop Stop the installed service (requires sudo)
status Show status of the autossh service
ACTIONS FOR HUB:
show-cmd Show the manual rclone mount command
install Install the rclone user service (no sudo)
start Start the rclone user service
stop Stop the rclone user service
status Show status of the rclone user service
mount Manual foreground mount (for testing)
unmount Unmount manually (fusermount -u)
EXAMPLES:
$0 spoke show-cmd
$0 spoke install
$0 hub install
$0 hub start
EOF
}
die() {
echo "ERROR: $*" >&2
exit 1
}
check_service_file() {
local file="$1"
if [ ! -f "$file" ]; then
die "Service template not found: $file"
fi
}
# ------------------------------------------------------------
# Spoke actions
# ------------------------------------------------------------
spoke_show_cmd() {
cat <<EOF
Run this command manually on the SPOKE to establish the reverse tunnel:
autossh -M 0 -NT -o "ServerAliveInterval 60" -o "ServerAliveCountMax 3" \\
-R ${TUNNEL_PORT}:localhost:22 -i ${SPOKE_SSH_KEY} ${HUB_USER}@${HUB_HOST}
EOF
}
spoke_install() {
check_service_file "$SPOKE_AUTOSSH_SERVICE_SRC"
echo "Installing autossh system service on SPOKE..."
sudo cp "$SPOKE_AUTOSSH_SERVICE_SRC" /etc/systemd/system/autossh-tunnel.service
sudo systemctl daemon-reload
echo "Service installed. Enable with: sudo systemctl enable autossh-tunnel.service"
echo "Start with: sudo systemctl start autossh-tunnel.service"
}
spoke_start() {
sudo systemctl start autossh-tunnel.service
echo "Started."
}
spoke_stop() {
sudo systemctl stop autossh-tunnel.service
echo "Stopped."
}
spoke_status() {
sudo systemctl status autossh-tunnel.service --no-pager
}
# ------------------------------------------------------------
# Hub actions
# ------------------------------------------------------------
hub_show_cmd() {
cat <<EOF
Manual rclone mount command on HUB (run in foreground, Ctrl+C to stop):
mkdir -p ${MOUNT_POINT}
rclone mount ${RCLONE_REMOTE_NAME}: ${MOUNT_POINT} \\
--config ${HOME}/.config/rclone/rclone.conf \\
--vfs-cache-mode writes \\
--allow-other
EOF
}
hub_install() {
check_service_file "$HUB_RCLONE_SERVICE_SRC"
echo "Installing rclone user service on HUB..."
mkdir -p "$HOME/.config/systemd/user"
cp "$HUB_RCLONE_SERVICE_SRC" "$HOME/.config/systemd/user/rclone-mount@.service"
systemctl --user daemon-reload
echo "Service installed. Enable with: systemctl --user enable rclone-mount@${RCLONE_REMOTE_NAME}.service"
echo "Start with: systemctl --user start rclone-mount@${RCLONE_REMOTE_NAME}.service"
}
hub_start() {
systemctl --user start "rclone-mount@${RCLONE_REMOTE_NAME}.service"
echo "Started."
}
hub_stop() {
systemctl --user stop "rclone-mount@${RCLONE_REMOTE_NAME}.service"
echo "Stopped."
}
hub_status() {
systemctl --user status "rclone-mount@${RCLONE_REMOTE_NAME}.service" --no-pager
}
hub_mount() {
mkdir -p "$MOUNT_POINT"
echo "Mounting in foreground. Press Ctrl+C to unmount."
rclone mount "${RCLONE_REMOTE_NAME}:" "$MOUNT_POINT" \
--config "${HOME}/.config/rclone/rclone.conf" \
--vfs-cache-mode writes \
--allow-other
}
hub_unmount() {
fusermount -u "$MOUNT_POINT" 2>/dev/null && echo "Unmounted." || echo "Not mounted or already unmounted."
}
# ------------------------------------------------------------
# Main dispatch
# ------------------------------------------------------------
if [ $# -lt 2 ]; then
usage
exit 1
fi
ROLE="$1"
ACTION="$2"
case "$ROLE" in
spoke)
case "$ACTION" in
show-cmd) spoke_show_cmd ;;
install) spoke_install ;;
start) spoke_start ;;
stop) spoke_stop ;;
status) spoke_status ;;
*) die "Unknown action for spoke: $ACTION" ;;
esac
;;
hub)
case "$ACTION" in
show-cmd) hub_show_cmd ;;
install) hub_install ;;
start) hub_start ;;
stop) hub_stop ;;
status) hub_status ;;
mount) hub_mount ;;
unmount) hub_unmount ;;
*) die "Unknown action for hub: $ACTION" ;;
esac
;;
*)
usage
exit 1
;;
esac