diff --git a/seedbox/install.sh b/seedbox/install.sh index 17af045..559f426 100644 --- a/seedbox/install.sh +++ b/seedbox/install.sh @@ -1,6 +1,9 @@ #!/bin/bash -# install.sh - Automated deployment of Seedbox Server with Transmission -# Usage: NFS_SERVER= curl -fsSL https://gitea.arnodo.fr/Damien/infra-scripts/raw/branch/main/seedbox/install.sh | bash +# install.sh - Seedbox Server Initial Setup +# Usage: curl -fsSL https://gitea.arnodo.fr/Damien/infra-scripts/raw/branch/main/seedbox/install.sh | bash +# +# This script prepares the server for Docker-based seedbox deployment. +# Services are deployed separately via Gitea Actions. set -euo pipefail @@ -14,6 +17,16 @@ log_info() { echo -e "${GREEN}[INFO]${NC} $1"; } log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; } log_error() { echo -e "${RED}[ERROR]${NC} $1"; } +# Configuration (can be overridden via environment) +HOSTNAME="${SEEDBOX_HOSTNAME:-seedbox}" +NFS_SERVER="${NFS_SERVER:-}" +NFS_SHARE_MEDIA="${NFS_SHARE_MEDIA:-/volume2/Multimédia}" +NFS_MOUNT_MEDIA="${NFS_MOUNT_MEDIA:-/mnt/media}" +NFS_OPTS="defaults,_netdev,nofail,x-systemd.automount,x-systemd.mount-timeout=30s" +SEEDBOX_DIR="/srv/seedbox" +DOWNLOADS_DIR="${SEEDBOX_DIR}/downloads" +REPO_URL="${REPO_URL:-https://gitea.arnodo.fr/Damien/infra-scripts.git}" + # Pre-flight checks check_root() { if [[ $EUID -eq 0 ]]; then @@ -32,83 +45,57 @@ check_debian() { fi } -check_required_vars() { - if [[ -z "${NFS_SERVER:-}" ]]; then - log_error "NFS_SERVER environment variable is required." - log_error "Usage: NFS_SERVER= curl -fsSL ... | bash" - exit 1 - fi -} - -# Generate random password if not provided -# Note: uses head -c with || true to avoid SIGPIPE with pipefail -generate_password() { - head -c 100 /dev/urandom | tr -dc 'A-Za-z0-9' | head -c 16 || true -} - -# Get Tailscale FQDN hostname from Self.DNSName -# Extracts DNSName from the Self object in tailscale status --json -get_tailscale_hostname() { - local dns_name - # Use awk to extract Self.DNSName specifically (not from peers) - # Looks for DNSName within the Self block - dns_name=$(tailscale status --json 2>/dev/null | awk -F'"' ' - /"Self"/ { in_self=1 } - in_self && /"DNSName"/ { gsub(/\.$/, "", $4); print $4; exit } - ' || true) - - if [[ -z "$dns_name" ]]; then - # Fallback: use hostname + default tailnet domain hint - dns_name="${HOSTNAME}.ts.net" - log_warn "Could not determine Tailscale FQDN, using fallback: $dns_name" - fi - echo "$dns_name" -} - -# Configuration variables (can be overridden via environment) -HOSTNAME="${SEEDBOX_HOSTNAME:-seedbox}" -# NFS shares configuration -NFS_SHARE_DOWNLOAD="${NFS_SHARE_DOWNLOAD:-/volume2/Downloads}" -NFS_SHARE_MEDIA="${NFS_SHARE_MEDIA:-/volume2/Multimédia}" -NFS_MOUNT_DOWNLOAD="${NFS_MOUNT_DOWNLOAD:-/mnt/download}" -NFS_MOUNT_MEDIA="${NFS_MOUNT_MEDIA:-/mnt/media}" -# NFS mount options (same as Proxmox) -NFS_OPTS="defaults,_netdev,nofail,x-systemd.automount,x-systemd.mount-timeout=30s" -PEER_PORT="${PEER_PORT:-51413}" -TIMEZONE="${TZ:-Europe/Paris}" -TRANSMISSION_USER="${TRANSMISSION_USER:-admin}" -TRANSMISSION_PASS="${TRANSMISSION_PASS:-$(generate_password)}" -TRANSMISSION_DIR="$HOME/transmission" - main() { - log_info "=== Seedbox Server Deployment ===" + log_info "=== Seedbox Server Setup ===" check_root check_debian - check_required_vars + # Step 1: System update + log_info "Updating system..." + sudo apt-get update -qq + sudo apt-get upgrade -y -qq + + # Step 2: Set hostname log_info "Setting hostname to: $HOSTNAME" echo "$HOSTNAME" | sudo tee /etc/hostname > /dev/null sudo hostnamectl set-hostname "$HOSTNAME" + # Step 3: Install base packages log_info "Installing base packages..." - sudo apt-get update -qq - sudo apt-get install -y -qq vim ca-certificates curl gnupg lsb-release fail2ban unattended-upgrades nfs-common ufw at > /dev/null + sudo apt-get install -y -qq \ + vim \ + ca-certificates \ + curl \ + gnupg \ + lsb-release \ + fail2ban \ + unattended-upgrades \ + nfs-common \ + ufw \ + at \ + git \ + > /dev/null + # Step 4: Install Tailscale log_info "Installing Tailscale..." curl -fsSL https://tailscale.com/install.sh | sh - log_info "Connecting to Tailscale..." + log_info "Connecting to Tailscale (SSH only)..." sudo tailscale up --ssh + + # Get Tailscale hostname for display + TS_FQDN=$(tailscale status --json 2>/dev/null | awk -F'"' ' + /"Self"/ { in_self=1 } + in_self && /"DNSName"/ { gsub(/\.$/, "", $4); print $4; exit } + ' || echo "${HOSTNAME}.ts.net") + # Step 5: Install Docker log_info "Installing Docker..." - # Use official Docker installation method (DEB822 format) - # See: https://docs.docker.com/engine/install/debian/ sudo install -m 0755 -d /etc/apt/keyrings sudo curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc sudo chmod a+r /etc/apt/keyrings/docker.asc - # Add Docker repository using DEB822 format (.sources) # shellcheck disable=SC1091 sudo tee /etc/apt/sources.list.d/docker.sources > /dev/null < /dev/null + sudo apt-get install -y -qq \ + docker-ce \ + docker-ce-cli \ + containerd.io \ + docker-buildx-plugin \ + docker-compose-plugin \ + > /dev/null + # Step 6: Configure Docker for user log_info "Adding current user to docker group..." sudo usermod -aG docker "$USER" - log_info "Configuring NFS mounts..." - sudo mkdir -p "$NFS_MOUNT_DOWNLOAD" "$NFS_MOUNT_MEDIA" - - # Add download share to fstab if not already present - if ! grep -q "${NFS_SERVER}:${NFS_SHARE_DOWNLOAD}" /etc/fstab; then - log_info "Adding NFS mount: ${NFS_SERVER}:${NFS_SHARE_DOWNLOAD} -> ${NFS_MOUNT_DOWNLOAD}" - echo "${NFS_SERVER}:${NFS_SHARE_DOWNLOAD} ${NFS_MOUNT_DOWNLOAD} nfs ${NFS_OPTS} 0 0" | sudo tee -a /etc/fstab > /dev/null - fi - - # Add media share to fstab if not already present - if ! grep -q "${NFS_SERVER}:${NFS_SHARE_MEDIA}" /etc/fstab; then - log_info "Adding NFS mount: ${NFS_SERVER}:${NFS_SHARE_MEDIA} -> ${NFS_MOUNT_MEDIA}" - echo "${NFS_SERVER}:${NFS_SHARE_MEDIA} ${NFS_MOUNT_MEDIA} nfs ${NFS_OPTS} 0 0" | sudo tee -a /etc/fstab > /dev/null - fi - - # Mount NFS shares - sudo mount -a || log_warn "NFS mount failed - ensure NAS is accessible via Tailscale" - - log_info "Creating Transmission stack..." - mkdir -p "$TRANSMISSION_DIR" - - cat > "$TRANSMISSION_DIR/docker-compose.yml" << EOF -services: - transmission: - image: linuxserver/transmission:latest - container_name: transmission - restart: unless-stopped - ports: - # Peer port - public for seeding - - "${PEER_PORT}:${PEER_PORT}" - - "${PEER_PORT}:${PEER_PORT}/udp" - # WebUI - bound to localhost only (exposed via tailscale serve) - - "127.0.0.1:9091:9091" - volumes: - - ./config:/config - # Downloads: incomplete and complete torrents - - ${NFS_MOUNT_DOWNLOAD}:/downloads - # Media: final destination for completed ISOs and images - - ${NFS_MOUNT_MEDIA}:/media - environment: - - PUID=1000 - - PGID=1000 - - TZ=${TIMEZONE} - - USER=${TRANSMISSION_USER} - - PASS=${TRANSMISSION_PASS} - - PEERPORT=${PEER_PORT} -EOF - - log_info "Starting Transmission..." - cd "$TRANSMISSION_DIR" - # Use sg to run docker compose with the new docker group membership - sg docker -c "docker compose up -d" - - log_info "Exposing Transmission WebUI via Tailscale..." - sudo tailscale serve --bg http://localhost:9091 - - # Get Tailscale hostname for final message - TS_HOSTNAME=$(get_tailscale_hostname) - + # Step 7: Configure UFW firewall log_info "Configuring UFW firewall..." sudo ufw --force reset > /dev/null sudo ufw default deny incoming > /dev/null sudo ufw default allow outgoing > /dev/null - # Allow BitTorrent peer port from public internet - sudo ufw allow ${PEER_PORT}/tcp > /dev/null - sudo ufw allow ${PEER_PORT}/udp > /dev/null + # BitTorrent peer port (public) + sudo ufw allow 51413/tcp > /dev/null + sudo ufw allow 51413/udp > /dev/null # Allow all traffic on Tailscale interface sudo ufw allow in on tailscale0 > /dev/null - # Temporarily allow SSH during setup (safety net) + # Temporary SSH access (safety net) sudo ufw allow 22/tcp > /dev/null sudo ufw --force enable > /dev/null # Schedule SSH rule removal in 5 minutes log_warn "SSH port 22 temporarily open for 5 minutes (safety net)." - log_warn "Verify Tailscale SSH access works, then wait or run: sudo ufw delete allow 22/tcp" - echo "sudo ufw delete allow 22/tcp && logger 'UFW: SSH port 22 closed by scheduled task'" | sudo at now + 5 minutes 2>/dev/null || { - log_warn "Could not schedule automatic SSH cleanup. Run manually after verification:" + echo "sudo ufw delete allow 22/tcp && logger 'UFW: SSH port 22 closed'" | sudo at now + 5 minutes 2>/dev/null || { + log_warn "Could not schedule automatic SSH cleanup. Run manually:" log_warn " sudo ufw delete allow 22/tcp" } + # Step 8: Create directory structure + log_info "Creating directory structure..." + sudo mkdir -p "$SEEDBOX_DIR" + sudo mkdir -p "$DOWNLOADS_DIR" + sudo chown -R "$USER:$USER" "$SEEDBOX_DIR" + + # Step 9: Configure NFS mount (if NFS_SERVER provided) + if [[ -n "$NFS_SERVER" ]]; then + log_info "Configuring NFS mount..." + sudo mkdir -p "$NFS_MOUNT_MEDIA" + + if ! grep -q "${NFS_SERVER}:${NFS_SHARE_MEDIA}" /etc/fstab; then + log_info "Adding NFS mount: ${NFS_SERVER}:${NFS_SHARE_MEDIA} -> ${NFS_MOUNT_MEDIA}" + echo "${NFS_SERVER}:${NFS_SHARE_MEDIA} ${NFS_MOUNT_MEDIA} nfs ${NFS_OPTS} 0 0" | sudo tee -a /etc/fstab > /dev/null + fi + + sudo mount -a || log_warn "NFS mount failed - ensure NAS is accessible via Tailscale" + else + log_warn "NFS_SERVER not set. NFS mount skipped. Set it later if needed." + fi + + # Step 10: Clone repository + log_info "Cloning infra-scripts repository..." + if [[ -d "${SEEDBOX_DIR}/.git" ]]; then + cd "$SEEDBOX_DIR" + git pull origin main || log_warn "Git pull failed" + else + git clone "$REPO_URL" "${SEEDBOX_DIR}/repo-tmp" + mv "${SEEDBOX_DIR}/repo-tmp/seedbox/"* "$SEEDBOX_DIR/" 2>/dev/null || true + mv "${SEEDBOX_DIR}/repo-tmp/seedbox/".* "$SEEDBOX_DIR/" 2>/dev/null || true + rm -rf "${SEEDBOX_DIR}/repo-tmp" + cd "$SEEDBOX_DIR" + git init + git remote add origin "$REPO_URL" + git fetch origin + git checkout -b main --track origin/main -- seedbox/ 2>/dev/null || true + fi + + # Step 11: Configure MOTD log_info "Configuring MOTD..." sudo chmod -x /etc/update-motd.d/* 2>/dev/null || true cat << 'MOTD' | sudo tee /etc/update-motd.d/00-seedbox > /dev/null #!/bin/bash -# Get Tailscale FQDN from Self.DNSName TS_FQDN=$(tailscale status --json 2>/dev/null | awk -F'"' ' /"Self"/ { in_self=1 } in_self && /"DNSName"/ { gsub(/\.$/, "", $4); print $4; exit } @@ -223,49 +196,56 @@ echo "\___ \| _| | _| | | | | _ \| | | \ /" echo " ___) | |___| |___| |_| | |_) | |_| / \\" echo "|____/|_____|_____|____/|____/ \___/_/\_\\" echo "" -echo "ISO Seedbox Server - Transmission" +echo "Docker Seedbox Server" echo "─────────────────────────────────────────" echo "Access:" -echo " • WebUI : https://${TS_FQDN}" -echo " • SSH : Tailscale only" -echo " • Seeding : Public port 51413" +echo " • SSH : ${TS_FQDN}" +echo " • Seeding : Public port 51413" +echo "" +echo "Services: (via Tailscale)" +docker ps --format ' • {{.Names}} : {{.Status}}' 2>/dev/null || echo " Docker not running" echo "" echo "Storage:" -echo " • Downloads : /mnt/download" -echo " • Media : /mnt/media" +echo " • Downloads : /srv/seedbox/downloads" +echo " • Media : /mnt/media (NFS)" echo "" echo "Useful commands:" -echo " cd ~/transmission && docker compose logs -f" -echo " df -h /mnt/download /mnt/media" +echo " cd /srv/seedbox && docker compose ls" +echo " docker logs -f " echo "─────────────────────────────────────────" echo "" MOTD sudo chmod +x /etc/update-motd.d/00-seedbox + # Final summary echo "" log_info "==========================================" - log_info "Deployment complete!" + log_info "Server setup complete!" log_info "==========================================" echo "" - echo "Transmission WebUI:" - echo " URL : https://${TS_HOSTNAME}" - echo " Username : ${TRANSMISSION_USER}" - echo " Password : ${TRANSMISSION_PASS}" + echo "Server accessible at:" + echo " SSH: ${TS_FQDN}" echo "" - echo "Storage (NFS mounts):" - echo " Downloads : ${NFS_SERVER}:${NFS_SHARE_DOWNLOAD} -> ${NFS_MOUNT_DOWNLOAD}" - echo " Media : ${NFS_SERVER}:${NFS_SHARE_MEDIA} -> ${NFS_MOUNT_MEDIA}" + echo "Directory structure:" + echo " ${SEEDBOX_DIR}/" + echo " ├── stacks/ # Docker Compose stacks" + echo " ├── downloads/ # Local downloads (SSD)" + echo " └── .env # Secrets (created by Gitea Actions)" echo "" - echo "Transmission paths:" - echo " /downloads -> ${NFS_MOUNT_DOWNLOAD} (incomplete + complete torrents)" - echo " /media -> ${NFS_MOUNT_MEDIA} (final destination for ISOs)" + echo "NFS mount:" + if [[ -n "$NFS_SERVER" ]]; then + echo " ${NFS_SERVER}:${NFS_SHARE_MEDIA} -> ${NFS_MOUNT_MEDIA}" + else + echo " Not configured. Run with NFS_SERVER= to configure." + fi echo "" - echo "Peer port : ${PEER_PORT} (public)" + echo "Next steps:" + echo " 1. Configure Gitea secrets (see README.md)" + echo " 2. Push to main branch to trigger deployment" + echo " 3. Services will be available at .taila5ad8.ts.net" echo "" log_warn "SSH port 22 will be closed in 5 minutes." - log_warn "To cancel: sudo atq (list jobs) then sudo atrm " - echo "" - echo "Save these credentials! The password was auto-generated." + log_warn "Use Tailscale SSH: ssh ${TS_FQDN}" echo "" }