From 74e1a9d1a08fcdb97b3ee0ade4d78658991fca13 Mon Sep 17 00:00:00 2001 From: Justin Oros Date: Sat, 18 Apr 2026 14:04:24 -0700 Subject: [PATCH] offboard-spoke.sh: run as root, remove sudo from python3 install --- hub/offboard-spoke.sh | 16 ++++++++++++++-- hub/setup-hub.sh | 5 +++-- spoke/Dockerfile | 4 ---- spoke/compose.yaml | 1 - spoke/setup-network.sh | 8 ++++---- spoke/setup-spoke.sh | 43 +++++++++++++++++++++++++++++------------- 6 files changed, 51 insertions(+), 26 deletions(-) diff --git a/hub/offboard-spoke.sh b/hub/offboard-spoke.sh index 06884e5..1eb3bdd 100755 --- a/hub/offboard-spoke.sh +++ b/hub/offboard-spoke.sh @@ -28,8 +28,20 @@ check_deps() { fi } -if [ "$(id -u)" -eq 0 ]; then - die "Run as the hub user, not root." +[ "$(id -u)" -eq 0 ] || die "Run as root" + +if ! command -v python3 >/dev/null 2>&1; then + if command -v apt-get >/dev/null 2>&1; then + apt-get install -y -q python3 + elif command -v dnf >/dev/null 2>&1; then + dnf install -y -q python3 + elif command -v yum >/dev/null 2>&1; then + yum install -y -q python3 + elif command -v pacman >/dev/null 2>&1; then + pacman -S --noconfirm python + else + die "python3 not found and no supported package manager to install it" + fi fi check_deps rclone crontab python3 diff --git a/hub/setup-hub.sh b/hub/setup-hub.sh index e7cbad1..e336081 100755 --- a/hub/setup-hub.sh +++ b/hub/setup-hub.sh @@ -162,12 +162,13 @@ for DIRECTIVE in "GatewayPorts yes" "AllowTcpForwarding yes" "ClientAliveInterva done SSH_SVC="" -if systemctl list-unit-files ssh.service >/dev/null 2>&1 && systemctl enable ssh 2>/dev/null; then +if systemctl list-unit-files ssh.service >/dev/null 2>&1; then SSH_SVC="ssh" -elif systemctl list-unit-files sshd.service >/dev/null 2>&1 && systemctl enable sshd 2>/dev/null; then +elif systemctl list-unit-files sshd.service >/dev/null 2>&1; then SSH_SVC="sshd" fi if [ -n "$SSH_SVC" ]; then + systemctl enable "$SSH_SVC" 2>/dev/null || true systemctl restart "$SSH_SVC" info "SSH server restarted." else diff --git a/spoke/Dockerfile b/spoke/Dockerfile index a4be890..04b7a1c 100644 --- a/spoke/Dockerfile +++ b/spoke/Dockerfile @@ -1,10 +1,6 @@ FROM debian:bookworm-slim RUN apt-get update && apt-get install -y autossh openssh-client && rm -rf /var/lib/apt/lists/* -RUN echo "PermitRootLogin no" >> /etc/ssh/sshd_config -RUN echo "PasswordAuthentication no" >> /etc/ssh/sshd_config -RUN echo "Subsystem sftp internal-sftp" >> /etc/ssh/sshd_config - ARG UID=1000 ARG GID=1000 RUN groupadd -g ${GID} armbian && useradd -m -u ${UID} -g armbian armbian diff --git a/spoke/compose.yaml b/spoke/compose.yaml index 002b123..4ba46b9 100644 --- a/spoke/compose.yaml +++ b/spoke/compose.yaml @@ -1,4 +1,3 @@ -version: '3.8' services: autossh: image: spoke-autossh diff --git a/spoke/setup-network.sh b/spoke/setup-network.sh index 128b035..b744ff5 100755 --- a/spoke/setup-network.sh +++ b/spoke/setup-network.sh @@ -78,7 +78,7 @@ DNS_YAML="" IFS=',' read -ra DNS_LIST <<< "$DNS_INPUT" for DNS in "${DNS_LIST[@]}"; do DNS=$(echo "$DNS" | tr -d ' ') - DNS_YAML="${DNS_YAML} - ${DNS}\n" + DNS_YAML="${DNS_YAML} - ${DNS}"$'\n' done info "Current netplan configs:" @@ -113,7 +113,7 @@ if $IS_WIFI; then [ -n "$WIFI_PASS" ] || die "Password cannot be empty" else WIFI_SSID="$CURRENT_SSID" - WIFI_PASS=$(grep -A2 "\"${WIFI_SSID}\"" "$NETPLAN_FILE" 2>/dev/null | grep password | awk -F': ' '{print $2}' | tr -d '"' || true) + WIFI_PASS=$(grep -FA2 "\"${WIFI_SSID}\"" "$NETPLAN_FILE" 2>/dev/null | grep password | awk -F': ' '{print $2}' | tr -d '"' || true) [ -n "$WIFI_PASS" ] || die "Could not extract WiFi password from existing config — please re-enter credentials." fi fi @@ -140,7 +140,7 @@ network: via: ${GATEWAY} nameservers: addresses: -$(printf '%b' "$DNS_YAML") access-points: +${DNS_YAML} access-points: "${WIFI_SSID}": password: "${WIFI_PASS}" NETEOF @@ -158,7 +158,7 @@ network: via: ${GATEWAY} nameservers: addresses: -$(printf '%b' "$DNS_YAML") +${DNS_YAML} NETEOF fi diff --git a/spoke/setup-spoke.sh b/spoke/setup-spoke.sh index c28e1e2..9721de5 100755 --- a/spoke/setup-spoke.sh +++ b/spoke/setup-spoke.sh @@ -157,12 +157,13 @@ usermod -aG docker "$SPOKE_USER" 2>/dev/null || true info "Enabling SSH server..." SSH_SVC="" -if systemctl list-unit-files ssh.service >/dev/null 2>&1 && systemctl enable ssh 2>/dev/null; then +if systemctl list-unit-files ssh.service >/dev/null 2>&1; then SSH_SVC="ssh" -elif systemctl list-unit-files sshd.service >/dev/null 2>&1 && systemctl enable sshd 2>/dev/null; then +elif systemctl list-unit-files sshd.service >/dev/null 2>&1; then SSH_SVC="sshd" fi if [ -n "$SSH_SVC" ]; then + systemctl enable "$SSH_SVC" 2>/dev/null || true systemctl start "$SSH_SVC" else warn "Could not enable SSH service — please start it manually." @@ -322,14 +323,13 @@ sed -i "s|-R [0-9]*:localhost:22|-R ${TUNNEL_PORT}:localhost:22|g" "$COMPOSE" sed -i "s|-i /home/[^ ]*/\.ssh/[^ ]*|-i ${SSH_DIR}/${KEY_NAME}|g" "$COMPOSE" sed -i "/known_hosts/!s|/home/[^/]*/\.ssh/[^:]*:/home/[^/]*/\.ssh/[^:]*:ro|${SSH_DIR}/${KEY_NAME}:${SSH_DIR}/${KEY_NAME}:ro|g" "$COMPOSE" sed -i "s|/home/[^/]*/\.ssh/known_hosts|${SSH_DIR}/known_hosts|g" "$COMPOSE" -sed -i "s|[a-zA-Z0-9._-]*@[a-zA-Z0-9._-]*\.[a-zA-Z0-9._-]*$|${HUB_USER}@${HUB_HOST}|g" "$COMPOSE" +sed -i "s| [a-zA-Z0-9._-]*@[a-zA-Z0-9._-]*\.[a-zA-Z0-9._-]*$| ${HUB_USER}@${HUB_HOST}|g" "$COMPOSE" sed -i "s|/home/[^/]*/st:|${SYNCTHING_MOUNT}:|g" "$COMPOSE" sed -i "s|PUID=[0-9]*|PUID=${SPOKE_UID}|g" "$COMPOSE" sed -i "s|PGID=[0-9]*|PGID=${SPOKE_GID}|g" "$COMPOSE" sed -i "s|container_name: spoke-autossh|container_name: ${SPOKE_NAME}-autossh|g" "$COMPOSE" sed -i "s|container_name: spoke-syncthing|container_name: ${SPOKE_NAME}-syncthing|g" "$COMPOSE" sed -i "s|hostname: spoke-syncthing|hostname: ${SPOKE_NAME}-syncthing|g" "$COMPOSE" -sed -i '/^version:/d' "$COMPOSE" header "Building Docker Image" @@ -340,17 +340,34 @@ docker build \ -t spoke-autossh . header "Starting Containers" -docker compose up -d -info "Waiting for tunnel to establish..." -sleep 6 +TUNNEL_UP=false +for ATTEMPT in 1 2 3; do + docker compose up -d + info "Waiting for tunnel to establish..." + sleep 6 + LOGS=$(docker logs "${SPOKE_NAME}-autossh" 2>&1 || true) + if echo "$LOGS" | grep -q "remote port forwarding failed"; then + warn "Tunnel failed on attempt $ATTEMPT — port $TUNNEL_PORT may have been taken." + docker compose down 2>/dev/null || true + NEXT_PORT=$((TUNNEL_PORT + 1)) + RESULT=$(sudo -u "$SPOKE_USER" ssh -i "$KEY_PATH" "$HUB_USER@$HUB_HOST" "ss -tlnp | grep :$NEXT_PORT" 2>/dev/null || true) + if [ -z "$RESULT" ]; then + TUNNEL_PORT=$NEXT_PORT + warn "Retrying with port $TUNNEL_PORT..." + sed -i "s|-R [0-9]*:localhost:22|-R ${TUNNEL_PORT}:localhost:22|g" "$COMPOSE" + else + warn "Next port also in use. Waiting before retry..." + fi + else + TUNNEL_UP=true + break + fi +done -LOGS=$(docker logs "${SPOKE_NAME}-autossh" 2>&1 || true) -if echo "$LOGS" | grep -q "remote port forwarding failed"; then - warn "Tunnel failed — port $TUNNEL_PORT may have been taken between check and connect." - warn "Try running: docker compose down && docker compose up -d" - warn "Or re-run this script." -else +if $TUNNEL_UP; then info "Tunnel is up on port $TUNNEL_PORT." +else + die "Tunnel failed after 3 attempts. Run: docker compose down && docker compose up -d" fi header "Setup Complete"