forked from finn/tinyboard
Compare commits
4 Commits
rclone-twe
...
onboard-sp
| Author | SHA1 | Date | |
|---|---|---|---|
| 5941f95b00 | |||
| 747c8a81d8 | |||
| 1f4e8555da | |||
| 56325a1b06 |
214
health-check.sh
214
health-check.sh
@@ -1,214 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
CYAN='\033[0;36m'
|
||||
NC='\033[0m'
|
||||
|
||||
ok() { echo -e " ${GREEN}[OK]${NC} $*"; }
|
||||
fail() { echo -e " ${RED}[FAIL]${NC} $*"; }
|
||||
warn() { echo -e " ${YELLOW}[WARN]${NC} $*"; }
|
||||
header() { echo -e "\n${CYAN}══════════════════════════════════════════${NC}"; echo -e "${CYAN} $*${NC}"; echo -e "${CYAN}══════════════════════════════════════════${NC}"; }
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
HUB_USER="${SUDO_USER:-${USER}}"
|
||||
if [ "$(id -u)" -eq 0 ] && [ -n "${SUDO_USER:-}" ]; then
|
||||
HUB_HOME=$(getent passwd "$SUDO_USER" | cut -d: -f6)
|
||||
else
|
||||
HUB_HOME="$HOME"
|
||||
fi
|
||||
if [ "$(id -u)" -eq 0 ] && [ "$HUB_HOME" = "/root" ]; then
|
||||
for u in armbian; do
|
||||
CANDIDATE=$(getent passwd "$u" 2>/dev/null | cut -d: -f6)
|
||||
if [ -f "${CANDIDATE}/.config/tinyboard/spokes" ]; then
|
||||
HUB_HOME="$CANDIDATE"
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi
|
||||
REGISTRY="${HUB_HOME}/.config/tinyboard/spokes"
|
||||
COMPOSE="$SCRIPT_DIR/spoke/compose.yaml"
|
||||
RCLONE_CONF="${HUB_HOME}/.config/rclone/rclone.conf"
|
||||
|
||||
IS_SPOKE=false
|
||||
IS_HUB=false
|
||||
|
||||
if docker ps --format '{{.Names}}' 2>/dev/null | grep -qi autossh; then
|
||||
IS_SPOKE=true
|
||||
fi
|
||||
|
||||
if [ -f "$REGISTRY" ]; then
|
||||
IS_HUB=true
|
||||
fi
|
||||
|
||||
if [ "$IS_SPOKE" = false ] && [ "$IS_HUB" = false ]; then
|
||||
echo -e "${YELLOW}Could not detect hub or spoke configuration. Is tinyboard set up?${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
check_common() {
|
||||
header "System"
|
||||
|
||||
local ssh_svc=""
|
||||
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; then
|
||||
ssh_svc="sshd"
|
||||
fi
|
||||
|
||||
if [ -n "$ssh_svc" ]; then
|
||||
if systemctl is-active "$ssh_svc" >/dev/null 2>&1; then
|
||||
ok "SSH server running ($ssh_svc)"
|
||||
else
|
||||
fail "SSH server not running ($ssh_svc)"
|
||||
fi
|
||||
else
|
||||
warn "Could not detect SSH service"
|
||||
fi
|
||||
|
||||
if [ "$IS_SPOKE" = true ]; then
|
||||
if command -v docker >/dev/null 2>&1; then
|
||||
ok "docker installed"
|
||||
else
|
||||
fail "docker not found"
|
||||
fi
|
||||
|
||||
if docker info >/dev/null 2>&1; then
|
||||
ok "docker daemon running"
|
||||
else
|
||||
fail "docker daemon not running"
|
||||
fi
|
||||
|
||||
local st_container
|
||||
st_container=$(docker ps --format '{{.Names}}' 2>/dev/null | grep -i syncthing | head -1 || true)
|
||||
if [ -n "$st_container" ]; then
|
||||
ok "Syncthing container running ($st_container)"
|
||||
if curl -sf http://127.0.0.1:8384 >/dev/null 2>&1; then
|
||||
ok "Syncthing API reachable"
|
||||
else
|
||||
warn "Syncthing container running but API not reachable on :8384"
|
||||
fi
|
||||
else
|
||||
warn "No Syncthing container running"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
check_spoke() {
|
||||
header "Spoke"
|
||||
|
||||
local autossh_container
|
||||
autossh_container=$(docker ps --format '{{.Names}}' 2>/dev/null | grep -i autossh | head -1 || true)
|
||||
if [ -n "$autossh_container" ]; then
|
||||
ok "autossh container running ($autossh_container)"
|
||||
|
||||
local logs
|
||||
logs=$(docker logs "$autossh_container" 2>&1 | tail -20 || true)
|
||||
if echo "$logs" | grep -q "remote port forwarding failed"; then
|
||||
fail "Tunnel reports port forwarding failed — check hub authorized_keys"
|
||||
else
|
||||
ok "No tunnel errors in recent logs"
|
||||
fi
|
||||
else
|
||||
fail "No autossh container running"
|
||||
fi
|
||||
|
||||
if [ -n "$autossh_container" ]; then
|
||||
local tunnel_port hub_host
|
||||
tunnel_port=$(docker inspect "$autossh_container" 2>/dev/null | python3 -c "
|
||||
import sys, json
|
||||
data = json.load(sys.stdin)
|
||||
cmd = ' '.join(data[0].get('Config', {}).get('Cmd', []))
|
||||
import re
|
||||
m = re.search(r'-R (\d+):localhost', cmd)
|
||||
print(m.group(1) if m else '')
|
||||
" 2>/dev/null || true)
|
||||
hub_host=$(docker inspect "$autossh_container" 2>/dev/null | python3 -c "
|
||||
import sys, json
|
||||
data = json.load(sys.stdin)
|
||||
cmd = ' '.join(data[0].get('Config', {}).get('Cmd', []))
|
||||
import re
|
||||
m = re.search(r'[a-zA-Z0-9._-]+@([a-zA-Z0-9._-]+\.[a-zA-Z0-9._-]+)', cmd)
|
||||
print(m.group(1) if m else '')
|
||||
" 2>/dev/null || true)
|
||||
if [ -n "$tunnel_port" ] && [ -n "$hub_host" ]; then
|
||||
ok "Tunnel configured: port $tunnel_port → $hub_host"
|
||||
else
|
||||
warn "Could not parse tunnel config from running container"
|
||||
fi
|
||||
fi
|
||||
|
||||
local st_data="/home/armbian/st/data"
|
||||
if [ -d "$st_data" ]; then
|
||||
ok "Syncthing data directory exists ($st_data)"
|
||||
else
|
||||
warn "Syncthing data directory not found ($st_data)"
|
||||
fi
|
||||
}
|
||||
|
||||
check_hub() {
|
||||
header "Hub"
|
||||
|
||||
local spoke_count
|
||||
spoke_count=$(wc -l < "$REGISTRY" 2>/dev/null || echo 0)
|
||||
ok "$spoke_count spoke(s) in registry"
|
||||
|
||||
while IFS= read -r line; do
|
||||
[ -n "$line" ] || continue
|
||||
local spoke_name tunnel_port key_path mount_point
|
||||
spoke_name=$(echo "$line" | awk '{print $1}')
|
||||
tunnel_port=$(echo "$line" | awk '{print $2}')
|
||||
key_path=$(echo "$line" | awk '{print $3}')
|
||||
mount_point=$(echo "$line" | awk '{print $4}')
|
||||
|
||||
echo ""
|
||||
echo -e " ${CYAN}Spoke: $spoke_name${NC}"
|
||||
|
||||
if ss -tlnp 2>/dev/null | grep -q ":${tunnel_port}"; then
|
||||
ok "Tunnel port $tunnel_port is listening"
|
||||
else
|
||||
fail "Tunnel port $tunnel_port not listening — is the spoke connected?"
|
||||
fi
|
||||
|
||||
if [ -f "$key_path" ]; then
|
||||
ok "Hub key exists ($key_path)"
|
||||
else
|
||||
fail "Hub key missing ($key_path)"
|
||||
fi
|
||||
|
||||
if mountpoint -q "$mount_point" 2>/dev/null; then
|
||||
ok "Mounted at $mount_point"
|
||||
else
|
||||
fail "Not mounted at $mount_point"
|
||||
fi
|
||||
|
||||
if grep -q "\[${spoke_name}-remote\]" "$RCLONE_CONF" 2>/dev/null; then
|
||||
ok "rclone remote [${spoke_name}-remote] configured"
|
||||
else
|
||||
fail "rclone remote [${spoke_name}-remote] not found in rclone.conf"
|
||||
fi
|
||||
|
||||
if crontab -u "$(basename "$HUB_HOME")" -l 2>/dev/null | grep -q "${spoke_name}-remote:"; then
|
||||
ok "Auto-mount crontab entry present"
|
||||
else
|
||||
warn "No auto-mount crontab entry for $spoke_name"
|
||||
fi
|
||||
|
||||
done < "$REGISTRY"
|
||||
|
||||
echo ""
|
||||
}
|
||||
|
||||
check_common
|
||||
|
||||
if [ "$IS_SPOKE" = true ]; then
|
||||
check_spoke
|
||||
fi
|
||||
|
||||
if [ "$IS_HUB" = true ]; then
|
||||
check_hub
|
||||
fi
|
||||
|
||||
echo ""
|
||||
@@ -82,6 +82,7 @@ mkdir -p "$(dirname "$RCLONE_CONF")"
|
||||
|
||||
header "Checking Tunnel"
|
||||
info "Verifying spoke SSH service is reachable on port $TUNNEL_PORT..."
|
||||
# Test TCP connectivity first
|
||||
if ! timeout 5 bash -c "cat < /dev/null > /dev/tcp/localhost/$TUNNEL_PORT" 2>/dev/null; then
|
||||
die "Cannot connect to port $TUNNEL_PORT on localhost — is the tunnel up?"
|
||||
fi
|
||||
@@ -95,7 +96,7 @@ while IFS= read -r KEYSCAN_LINE; do
|
||||
fi
|
||||
done <<<"$KEYSCAN"
|
||||
|
||||
header "Generating Hub-to-Spoke Access Key"
|
||||
header "Generating Hub SSH Key"
|
||||
if [ -f "$KEY_PATH" ]; then
|
||||
warn "Key $KEY_PATH already exists, skipping generation."
|
||||
else
|
||||
@@ -105,7 +106,7 @@ fi
|
||||
chmod 600 "$KEY_PATH"
|
||||
info "Permissions set: $KEY_PATH is 600"
|
||||
|
||||
header "Installing Hub-to-Spoke Access Key on Spoke"
|
||||
header "Installing Hub Access Key on Spoke"
|
||||
info "Copying hub public key to spoke's authorized_keys so the hub can SSH in for rclone..."
|
||||
info "(You will be prompted for the $SPOKE_USER password on the spoke)"
|
||||
if ssh-copy-id -i "$KEY_PATH.pub" -p "$TUNNEL_PORT" "$SPOKE_USER"@localhost; then
|
||||
@@ -121,7 +122,7 @@ else
|
||||
read -rp "Press ENTER once the key has been added to the spoke..."
|
||||
fi
|
||||
|
||||
header "Testing Hub-to-Spoke Key Auth"
|
||||
header "Testing Hub -> Spoke Key Auth"
|
||||
retry_or_abort \
|
||||
"ssh -i \"$KEY_PATH\" -o BatchMode=yes -o ConnectTimeout=10 -p \"$TUNNEL_PORT\" \"$SPOKE_USER\"@localhost exit" \
|
||||
"Key auth failed. Check authorized_keys on the spoke."
|
||||
@@ -132,12 +133,17 @@ if grep -q "\[${SPOKE_NAME}-remote\]" "$RCLONE_CONF" 2>/dev/null; then
|
||||
warn "Remote [${SPOKE_NAME}-remote] already exists in $RCLONE_CONF, skipping."
|
||||
else
|
||||
[ -s "$RCLONE_CONF" ] && tail -c1 "$RCLONE_CONF" | grep -qv $'\n' && echo "" >>"$RCLONE_CONF"
|
||||
python3 - "$RCLONE_CONF" "$SPOKE_NAME" "$TUNNEL_PORT" "$KEY_PATH" <<'PYEOF'
|
||||
import sys
|
||||
path, name, port, key = sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4]
|
||||
with open(path, 'a') as f:
|
||||
f.write(f"\n[{name}-remote]\ntype = sftp\nhost = localhost\nport = {port}\nkey_file = {key}\nshell_type = unix\nmd5sum_command = md5sum\nsha1sum_command = sha1sum\n")
|
||||
PYEOF
|
||||
cat >>"$RCLONE_CONF" <<EOF
|
||||
|
||||
[${SPOKE_NAME}-remote]
|
||||
type = sftp
|
||||
host = localhost
|
||||
port = $TUNNEL_PORT
|
||||
key_file = $KEY_PATH
|
||||
shell_type = unix
|
||||
md5sum_command = md5sum
|
||||
sha1sum_command = sha1sum
|
||||
EOF
|
||||
info "Remote [${SPOKE_NAME}-remote] added to $RCLONE_CONF."
|
||||
fi
|
||||
|
||||
@@ -147,7 +153,7 @@ ADD_UNION="${ADD_UNION:-n}"
|
||||
if [[ "${ADD_UNION,,}" == "y" ]]; then
|
||||
read -rp "Union remote name [shared-union]: " UNION_NAME
|
||||
UNION_NAME="${UNION_NAME:-shared-union}"
|
||||
read -rp "Subfolder path on the spoke being onboarded (e.g. books, leave blank for root): " UNION_PATH
|
||||
read -rp "Subfolder path on this spoke (e.g. books, leave blank for root): " UNION_PATH
|
||||
echo ""
|
||||
echo "Upstream access mode for this spoke:"
|
||||
echo " 0) None - full read/write (default)"
|
||||
@@ -174,7 +180,7 @@ if [[ "${ADD_UNION,,}" == "y" ]]; then
|
||||
fi
|
||||
if grep -q "^\[${UNION_NAME}\]" "$RCLONE_CONF" 2>/dev/null; then
|
||||
ALREADY=$(
|
||||
python3 - "$RCLONE_CONF" "$UNION_NAME" "${SPOKE_NAME}-remote:" <<'PYEOF2'
|
||||
python3 - "$RCLONE_CONF" "$UNION_NAME" "${SPOKE_NAME}-remote:" <<'PYEOF'
|
||||
import sys
|
||||
path, section, prefix = sys.argv[1], sys.argv[2], sys.argv[3]
|
||||
with open(path) as f:
|
||||
@@ -189,12 +195,12 @@ for line in lines:
|
||||
print("yes")
|
||||
sys.exit(0)
|
||||
print("no")
|
||||
PYEOF2
|
||||
PYEOF
|
||||
)
|
||||
if [ "$ALREADY" = "yes" ]; then
|
||||
warn "Upstream for ${SPOKE_NAME}-remote already in union remote [${UNION_NAME}], skipping."
|
||||
else
|
||||
python3 - "$RCLONE_CONF" "$UNION_NAME" "$UPSTREAM" <<'PYEOF2'
|
||||
python3 - "$RCLONE_CONF" "$UNION_NAME" "$UPSTREAM" <<'PYEOF'
|
||||
import sys
|
||||
path, section, upstream = sys.argv[1], sys.argv[2], sys.argv[3]
|
||||
with open(path) as f:
|
||||
@@ -211,7 +217,7 @@ for line in lines:
|
||||
out.append(line)
|
||||
with open(path, "w") as f:
|
||||
f.writelines(out)
|
||||
PYEOF2
|
||||
PYEOF
|
||||
info "Added '$UPSTREAM' to union remote [${UNION_NAME}]."
|
||||
fi
|
||||
else
|
||||
@@ -241,8 +247,7 @@ echo "${SPOKE_NAME} ${TUNNEL_PORT} ${KEY_PATH} ${MOUNT_POINT}" >>"$REGISTRY"
|
||||
info "$SPOKE_NAME registered."
|
||||
|
||||
header "Setting Up Auto-Mount"
|
||||
#MOUNT_CMD="rclone mount ${SPOKE_NAME}-remote: ${MOUNT_POINT} --config ${HOME}/.config/rclone/rclone.conf --vfs-cache-mode writes --allow-other --daemon"
|
||||
MOUNT_CMD="sleep 55 && rclone mount ${SPOKE_NAME}-remote: ${MOUNT_POINT} --config ${HOME}/.config/rclone/rclone.conf --vfs-cache-mode full --vfs-cache-max-size 2G --vfs-read-ahead 256M --allow-other --daemon"
|
||||
MOUNT_CMD="rclone mount ${SPOKE_NAME}-remote: ${MOUNT_POINT} --config ${HOME}/.config/rclone/rclone.conf --vfs-cache-mode writes --allow-other --daemon"
|
||||
CRON_ENTRY="@reboot ${MOUNT_CMD}"
|
||||
EXISTING=$(crontab -l 2>/dev/null || true)
|
||||
if echo "$EXISTING" | grep -qF "${SPOKE_NAME}-remote:"; then
|
||||
@@ -268,3 +273,6 @@ echo -e " Port: ${GREEN}$TUNNEL_PORT${NC}"
|
||||
echo -e " Hub key: ${GREEN}$KEY_PATH${NC}"
|
||||
echo -e " rclone: ${GREEN}${SPOKE_NAME}-remote${NC}"
|
||||
echo ""
|
||||
echo -e "${YELLOW}To mount this spoke:${NC}"
|
||||
echo " RCLONE_REMOTE=${SPOKE_NAME}-remote hubspoke-helper.sh hub start"
|
||||
echo ""
|
||||
|
||||
@@ -63,7 +63,7 @@ if command -v apt-get >/dev/null 2>&1; then
|
||||
PKG_MANAGER="apt"
|
||||
PKG_INSTALL="apt-get install -y -q"
|
||||
OPENSSH_PKG="openssh-server"
|
||||
FUSE_PKG="fuse3"
|
||||
FUSE_PKG="fuse"
|
||||
info "Detected: apt (Debian/Ubuntu)"
|
||||
apt-get update -q
|
||||
elif command -v dnf >/dev/null 2>&1; then
|
||||
@@ -136,7 +136,7 @@ header "SSH Server Configuration"
|
||||
SSHD_CONF="/etc/ssh/sshd_config"
|
||||
[ -f "$SSHD_CONF" ] || die "sshd_config not found at $SSHD_CONF"
|
||||
|
||||
for DIRECTIVE in "GatewayPorts no" "AllowTcpForwarding yes" "ClientAliveInterval 60" "ClientAliveCountMax 3"; do
|
||||
for DIRECTIVE in "GatewayPorts no" "AllowTcpForwarding local" "ClientAliveInterval 60" "ClientAliveCountMax 3"; do
|
||||
KEY="${DIRECTIVE%% *}"
|
||||
if grep -q "^$KEY" "$SSHD_CONF"; then
|
||||
sed -i "s|^$KEY.*|$DIRECTIVE|" "$SSHD_CONF"
|
||||
@@ -161,8 +161,8 @@ else
|
||||
fi
|
||||
|
||||
header "Password Authentication"
|
||||
read -rp "Disable password auth for $HUB_USER and use keys only? [y/N]: " DISABLE_PASS
|
||||
DISABLE_PASS="${DISABLE_PASS:-n}"
|
||||
read -rp "Disable password auth for $HUB_USER and use keys only? [Y/n]: " DISABLE_PASS
|
||||
DISABLE_PASS="${DISABLE_PASS:-y}"
|
||||
if [[ "${DISABLE_PASS,,}" == "y" ]]; then
|
||||
if [ ! -s "$SSH_DIR/authorized_keys" ]; then
|
||||
warn "No keys found in $SSH_DIR/authorized_keys — skipping password auth disable to avoid lockout."
|
||||
@@ -251,7 +251,7 @@ info "Mount point created at $MOUNT_POINT."
|
||||
|
||||
header "Hub Setup Complete"
|
||||
echo -e " Hub user: ${GREEN}$HUB_USER${NC}"
|
||||
echo -e " SSH config: ${GREEN}GatewayPorts no, AllowTcpForwarding yes, ClientAliveInterval 60${NC}"
|
||||
echo -e " SSH config: ${GREEN}GatewayPorts no, AllowTcpForwarding local, ClientAliveInterval 60${NC}"
|
||||
echo -e " FUSE: ${GREEN}user_allow_other enabled${NC}"
|
||||
echo -e " rclone config: ${GREEN}$RCLONE_CONF${NC}"
|
||||
echo -e " Mount point: ${GREEN}$MOUNT_POINT${NC}"
|
||||
|
||||
@@ -11,22 +11,25 @@ services:
|
||||
-o "ServerAliveInterval=60"
|
||||
-o "ServerAliveCountMax=3"
|
||||
-R 11111:localhost:22
|
||||
-i /home/armbian/.ssh/hubkey
|
||||
armbian@hub.example.com
|
||||
-i /home/armbian/.ssh/oilykey2026
|
||||
armbian@oily.dad
|
||||
volumes:
|
||||
- /home/armbian/.ssh/hubkey:/home/armbian/.ssh/hubkey:ro
|
||||
- /home/armbian/.ssh/oilykey2026:/home/armbian/.ssh/oilykey2026:ro
|
||||
- /home/armbian/.ssh/known_hosts:/home/armbian/.ssh/known_hosts:ro
|
||||
syncthing:
|
||||
image: syncthing/syncthing
|
||||
container_name: spoke-syncthing
|
||||
hostname: spoke-syncthing
|
||||
restart: unless-stopped
|
||||
user: "1000:1000"
|
||||
environment:
|
||||
- HOME=/var/syncthing
|
||||
- PUID=1000
|
||||
- PGID=1000
|
||||
ports:
|
||||
- "8384:8384"
|
||||
- "127.0.0.1:8384:8384"
|
||||
- "22000:22000"
|
||||
volumes:
|
||||
- /home/armbian/st/config:/var/syncthing/config
|
||||
- syncthing-config:/var/syncthing/config
|
||||
- /home/armbian/st/data:/var/syncthing/data
|
||||
|
||||
volumes:
|
||||
syncthing-config:
|
||||
|
||||
@@ -32,8 +32,6 @@ header "TinyBoard Network Setup"
|
||||
echo ""
|
||||
echo " 0) Change hostname"
|
||||
echo " 1) Configure static IP"
|
||||
echo " 2) Prefer IPv4 over IPv6"
|
||||
echo " 3) Prefer IPv6 over IPv4"
|
||||
echo " q) Quit"
|
||||
echo ""
|
||||
read -rp "Choose: " NET_OPT
|
||||
@@ -55,22 +53,6 @@ case "$NET_OPT" in
|
||||
;;
|
||||
1)
|
||||
;;
|
||||
2)
|
||||
header "Prefer IPv4 over IPv6"
|
||||
if grep -q "precedence ::ffff:0:0/96" /etc/gai.conf 2>/dev/null; then
|
||||
warn "IPv4 preference already set."
|
||||
else
|
||||
echo "precedence ::ffff:0:0/96 100" >> /etc/gai.conf
|
||||
info "IPv4 preference set. Outgoing connections will prefer IPv4."
|
||||
fi
|
||||
exit 0
|
||||
;;
|
||||
3)
|
||||
header "Prefer IPv6 over IPv4"
|
||||
sed -i '/precedence ::ffff:0:0\/96/d' /etc/gai.conf 2>/dev/null || true
|
||||
info "IPv4 preference removed. System will use default IPv6-first behavior."
|
||||
exit 0
|
||||
;;
|
||||
q|Q)
|
||||
exit 0
|
||||
;;
|
||||
@@ -177,19 +159,10 @@ PYEOF
|
||||
fi
|
||||
|
||||
header "Writing Netplan Config"
|
||||
BACKUP_FILE=""
|
||||
if [ -f "$NETPLAN_FILE" ]; then
|
||||
NETPLAN_BACKUP_DIR="/root/.config/tinyboard/netplan-backups"
|
||||
mkdir -p "$NETPLAN_BACKUP_DIR"
|
||||
BACKUP_FILE=""
|
||||
|
||||
for OTHER_FILE in /etc/netplan/*.yaml; do
|
||||
[ "$OTHER_FILE" = "$NETPLAN_FILE" ] && continue
|
||||
BACKUP_OTHER="$NETPLAN_BACKUP_DIR/$(basename "${OTHER_FILE}").$(date +%Y%m%d%H%M%S)"
|
||||
cp "$OTHER_FILE" "$BACKUP_OTHER"
|
||||
rm "$OTHER_FILE"
|
||||
warn "Removed conflicting netplan file: $OTHER_FILE (backed up to $BACKUP_OTHER)"
|
||||
done
|
||||
|
||||
if [ -f "$NETPLAN_FILE" ]; then
|
||||
BACKUP_FILE="$NETPLAN_BACKUP_DIR/$(basename "${NETPLAN_FILE}").$(date +%Y%m%d%H%M%S)"
|
||||
cp "$NETPLAN_FILE" "$BACKUP_FILE"
|
||||
info "Netplan config backed up to $BACKUP_FILE"
|
||||
|
||||
@@ -174,7 +174,7 @@ SSHD_CONF="/etc/ssh/sshd_config"
|
||||
header "Hostname Setup"
|
||||
CURRENT_HOSTNAME=$(hostname)
|
||||
echo -e "Current hostname: ${YELLOW}$CURRENT_HOSTNAME${NC}"
|
||||
read -rp "Enter a hostname for this spoke [${CURRENT_HOSTNAME}]: " SPOKE_NAME
|
||||
read -rp "Enter a hostname for this spoke (e.g. rocky, gouda, camembert): " SPOKE_NAME
|
||||
SPOKE_NAME="${SPOKE_NAME:-$CURRENT_HOSTNAME}"
|
||||
[[ "$SPOKE_NAME" =~ ^[a-zA-Z0-9._-]+$ ]] || die "Spoke name '$SPOKE_NAME' contains invalid characters. Use only letters, numbers, dots, underscores, hyphens."
|
||||
hostnamectl set-hostname "$SPOKE_NAME"
|
||||
@@ -215,10 +215,6 @@ case "$KEY_CHOICE" in
|
||||
cat "$KEY_PATH.pub"
|
||||
echo -e "${YELLOW}══════════════════════════════════════════${NC}"
|
||||
echo ""
|
||||
echo -e "${YELLOW} On the hub, run as ${HUB_USER}:${NC}"
|
||||
echo ""
|
||||
echo " echo "$(cat "$KEY_PATH.pub")" >> /home/${HUB_USER}/.ssh/authorized_keys"
|
||||
echo ""
|
||||
read -rp "Press ENTER once the key has been added to ${HUB_HOST}..."
|
||||
;;
|
||||
2)
|
||||
@@ -319,12 +315,6 @@ if [ -n "$HUB_KEYSCAN" ]; then
|
||||
if ! grep -qF "$KEYSCAN_KEY" "$SSH_DIR/known_hosts" 2>/dev/null; then
|
||||
echo "$KEYSCAN_LINE" >> "$SSH_DIR/known_hosts"
|
||||
fi
|
||||
mkdir -p /root/.ssh
|
||||
touch /root/.ssh/known_hosts
|
||||
chmod 600 /root/.ssh/known_hosts
|
||||
if ! grep -qF "$KEYSCAN_KEY" /root/.ssh/known_hosts 2>/dev/null; then
|
||||
echo "$KEYSCAN_LINE" >> /root/.ssh/known_hosts
|
||||
fi
|
||||
done <<< "$HUB_KEYSCAN"
|
||||
fi
|
||||
check_permissions "$SSH_DIR/known_hosts" "known_hosts"
|
||||
@@ -359,9 +349,8 @@ header "Configuring compose.yaml"
|
||||
info "Setting port to $TUNNEL_PORT and key to $KEY_NAME..."
|
||||
|
||||
SYNCTHING_MOUNT="$ARMBIAN_HOME/st/data"
|
||||
SYNCTHING_CONFIG="$ARMBIAN_HOME/st/config"
|
||||
mkdir -p "$SYNCTHING_MOUNT" "$SYNCTHING_CONFIG"
|
||||
chown "$SPOKE_USER":"$SPOKE_USER" "$SYNCTHING_MOUNT" "$SYNCTHING_CONFIG"
|
||||
mkdir -p "$SYNCTHING_MOUNT"
|
||||
chown "$SPOKE_USER":"$SPOKE_USER" "$SYNCTHING_MOUNT"
|
||||
|
||||
SPOKE_UID=$(id -u "$SPOKE_USER")
|
||||
SPOKE_GID=$(id -g "$SPOKE_USER")
|
||||
@@ -372,8 +361,8 @@ sed -i "/known_hosts/!s|/home/[^/]*/\.ssh/[^:]*:/home/[^/]*/\.ssh/[^:]*:ro|${SSH
|
||||
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._-]*[[:space:]]*\$| ${HUB_USER}@${HUB_HOST}|" "$COMPOSE"
|
||||
sed -i "s|/home/[^/]*/st/data:|${SYNCTHING_MOUNT}:|g" "$COMPOSE"
|
||||
sed -i "s|/home/[^/]*/st/config:|${SYNCTHING_CONFIG}:|g" "$COMPOSE"
|
||||
sed -i "s|user: \"[0-9]*:[0-9]*\"|user: \"${SPOKE_UID}:${SPOKE_GID}\"|" "$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"
|
||||
|
||||
@@ -19,7 +19,7 @@ ST_CONTAINER=""
|
||||
get_apikey() {
|
||||
ST_CONTAINER=$(docker ps --format '{{.Names}}' | grep -i syncthing | head -1 || true)
|
||||
[ -n "$ST_CONTAINER" ] || die "No running Syncthing container found."
|
||||
APIKEY=$(docker exec "$ST_CONTAINER" cat /var/syncthing/config/config.xml 2>/dev/null | grep -o '<apikey>[^<]*</apikey>' | sed 's/<[^>]*>//g' || true)
|
||||
APIKEY=$(docker exec "$ST_CONTAINER" python3 -c "import xml.etree.ElementTree as ET; print(ET.parse('/var/syncthing/config/config.xml').find('.//apikey').text)" 2>/dev/null || true)
|
||||
[ -n "$APIKEY" ] || die "Could not read Syncthing API key from container '$ST_CONTAINER'."
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user