From 528c86d6400a77b4c28a229e1a52a686ef8a2f34 Mon Sep 17 00:00:00 2001 From: Juan Denis Date: Wed, 21 Jan 2026 22:57:40 -0500 Subject: [PATCH] Add agent install scripts and release automation Introduced Linux (install.sh) and Windows (install.ps1) installation scripts for ServerKit agent, and exposed new backend endpoints to serve these scripts and installation instructions. Added a GitHub Actions workflow (agent-release.yml) for automated agent builds and releases. Updated ROADMAP.md and SERVERS_INTEGRATION_PLAN.md to reflect progress on installation and distribution features. --- .github/workflows/agent-release.yml | 203 ++++++++++++++++ ROADMAP.md | 19 +- SERVERS_INTEGRATION_PLAN.md | 180 +++++++------- backend/app/api/servers.py | 136 ++++++++++- scripts/install.ps1 | 323 ++++++++++++++++++++++++++ scripts/install.sh | 348 ++++++++++++++++++++++++++++ 6 files changed, 1115 insertions(+), 94 deletions(-) create mode 100644 .github/workflows/agent-release.yml create mode 100644 scripts/install.ps1 create mode 100644 scripts/install.sh diff --git a/.github/workflows/agent-release.yml b/.github/workflows/agent-release.yml new file mode 100644 index 00000000..4c3a2e6f --- /dev/null +++ b/.github/workflows/agent-release.yml @@ -0,0 +1,203 @@ +name: Agent Release + +on: + push: + tags: + - 'agent-v*' + workflow_dispatch: + inputs: + version: + description: 'Version to release (e.g., 1.0.0)' + required: true + type: string + +env: + GO_VERSION: '1.21' + +jobs: + build: + name: Build Agent + runs-on: ubuntu-latest + + strategy: + matrix: + include: + - goos: linux + goarch: amd64 + suffix: '' + - goos: linux + goarch: arm64 + suffix: '' + - goos: windows + goarch: amd64 + suffix: '.exe' + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: ${{ env.GO_VERSION }} + cache-dependency-path: agent/go.sum + + - name: Get version + id: version + run: | + if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then + VERSION="${{ github.event.inputs.version }}" + else + VERSION="${GITHUB_REF#refs/tags/agent-v}" + fi + echo "version=$VERSION" >> $GITHUB_OUTPUT + echo "Building version: $VERSION" + + - name: Build binary + env: + GOOS: ${{ matrix.goos }} + GOARCH: ${{ matrix.goarch }} + CGO_ENABLED: 0 + run: | + cd agent + go build -ldflags="-s -w -X main.Version=${{ steps.version.outputs.version }}" \ + -o ../dist/serverkit-agent-${{ matrix.goos }}-${{ matrix.goarch }}${{ matrix.suffix }} \ + ./cmd/agent + + - name: Compress binary (Linux) + if: matrix.goos == 'linux' + run: | + cd dist + tar -czvf serverkit-agent-${{ steps.version.outputs.version }}-${{ matrix.goos }}-${{ matrix.goarch }}.tar.gz \ + serverkit-agent-${{ matrix.goos }}-${{ matrix.goarch }} + + - name: Compress binary (Windows) + if: matrix.goos == 'windows' + run: | + cd dist + zip serverkit-agent-${{ steps.version.outputs.version }}-${{ matrix.goos }}-${{ matrix.goarch }}.zip \ + serverkit-agent-${{ matrix.goos }}-${{ matrix.goarch }}.exe + + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: serverkit-agent-${{ matrix.goos }}-${{ matrix.goarch }} + path: dist/serverkit-agent-${{ steps.version.outputs.version }}-* + retention-days: 5 + + release: + name: Create Release + runs-on: ubuntu-latest + needs: build + permissions: + contents: write + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Get version + id: version + run: | + if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then + VERSION="${{ github.event.inputs.version }}" + else + VERSION="${GITHUB_REF#refs/tags/agent-v}" + fi + echo "version=$VERSION" >> $GITHUB_OUTPUT + + - name: Download all artifacts + uses: actions/download-artifact@v4 + with: + path: artifacts + + - name: Prepare release files + run: | + mkdir -p release + find artifacts -type f \( -name "*.tar.gz" -o -name "*.zip" \) -exec mv {} release/ \; + ls -la release/ + + - name: Generate checksums + run: | + cd release + sha256sum * > checksums.txt + cat checksums.txt + + - name: Create Release + uses: softprops/action-gh-release@v1 + with: + tag_name: agent-v${{ steps.version.outputs.version }} + name: ServerKit Agent v${{ steps.version.outputs.version }} + draft: false + prerelease: false + files: | + release/* + body: | + ## ServerKit Agent v${{ steps.version.outputs.version }} + + ### Downloads + + | Platform | Architecture | Download | + |----------|--------------|----------| + | Linux | x64 (amd64) | `serverkit-agent-${{ steps.version.outputs.version }}-linux-amd64.tar.gz` | + | Linux | ARM64 | `serverkit-agent-${{ steps.version.outputs.version }}-linux-arm64.tar.gz` | + | Windows | x64 (amd64) | `serverkit-agent-${{ steps.version.outputs.version }}-windows-amd64.zip` | + + ### Quick Install + + **Linux (one-liner):** + ```bash + curl -fsSL https://your-serverkit.com/install.sh | sudo bash -s -- --token "YOUR_TOKEN" + ``` + + **Windows (PowerShell as Admin):** + ```powershell + irm https://your-serverkit.com/install.ps1 | iex; Install-ServerKitAgent -Token "YOUR_TOKEN" + ``` + + ### Verification + + Verify downloads using the checksums in `checksums.txt`: + ```bash + sha256sum -c checksums.txt + ``` + + ### What's New + + See [CHANGELOG](https://github.com/${{ github.repository }}/blob/main/agent/CHANGELOG.md) for details. + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + summary: + name: Build Summary + runs-on: ubuntu-latest + needs: [build, release] + + steps: + - name: Get version + id: version + run: | + if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then + VERSION="${{ github.event.inputs.version }}" + else + VERSION="${GITHUB_REF#refs/tags/agent-v}" + fi + echo "version=$VERSION" >> $GITHUB_OUTPUT + + - name: Create summary + run: | + echo "## Agent Release Summary" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Version:** v${{ steps.version.outputs.version }}" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "### Built Binaries" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "| Platform | Architecture | Status |" >> $GITHUB_STEP_SUMMARY + echo "|----------|--------------|--------|" >> $GITHUB_STEP_SUMMARY + echo "| Linux | amd64 | ✅ Built |" >> $GITHUB_STEP_SUMMARY + echo "| Linux | arm64 | ✅ Built |" >> $GITHUB_STEP_SUMMARY + echo "| Windows | amd64 | ✅ Built |" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "### Release" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "Release created at: https://github.com/${{ github.repository }}/releases/tag/agent-v${{ steps.version.outputs.version }}" >> $GITHUB_STEP_SUMMARY diff --git a/ROADMAP.md b/ROADMAP.md index bbf9f23d..18ea0222 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -117,16 +117,21 @@ This document outlines the development roadmap for ServerKit. Features are organ --- -## Phase 10: Multi-Server Management (Planned) +## Phase 10: Multi-Server Management (In Progress) **Priority: High** -- [ ] Agent-based remote server monitoring -- [ ] Centralized dashboard for multiple servers -- [ ] Server grouping and tagging -- [ ] Cross-server metrics comparison -- [ ] Remote command execution -- [ ] Server health overview +- [x] Agent-based remote server monitoring (Go agent) +- [x] Centralized dashboard for multiple servers +- [x] Server grouping and tagging +- [x] Cross-server metrics comparison +- [x] Remote Docker management via agents +- [x] Server health overview +- [x] Agent WebSocket gateway +- [x] HMAC-SHA256 authentication +- [x] GitHub Actions for agent releases (Linux/Windows) +- [x] Installation scripts endpoint +- [ ] Agent auto-update mechanism --- diff --git a/SERVERS_INTEGRATION_PLAN.md b/SERVERS_INTEGRATION_PLAN.md index 00c9077e..e1d4d466 100644 --- a/SERVERS_INTEGRATION_PLAN.md +++ b/SERVERS_INTEGRATION_PLAN.md @@ -1185,137 +1185,145 @@ system:exec: # Requires explicit permission ## 11. Phase Implementation Plan -### Phase 10.1: Foundation (Agent Core) +### Phase 10.1: Foundation (Agent Core) ✅ COMPLETED **Duration: ~2-3 weeks** ``` -[ ] Design and implement Go agent binary - [ ] Project setup with Go modules - [ ] Configuration loading (YAML) - [ ] Logging system (structured logs) - [ ] WebSocket client with reconnection - [ ] HMAC authentication - [ ] Heartbeat mechanism - [ ] Graceful shutdown handling +[x] Design and implement Go agent binary + [x] Project setup with Go modules + [x] Configuration loading (YAML) + [x] Logging system (structured logs) + [x] WebSocket client with reconnection + [x] HMAC authentication + [x] Heartbeat mechanism + [x] Graceful shutdown handling -[ ] Docker integration in agent - [ ] Connect to Docker socket - [ ] Container list/inspect - [ ] Container start/stop/restart - [ ] Basic stats collection +[x] Docker integration in agent + [x] Connect to Docker socket + [x] Container list/inspect + [x] Container start/stop/restart + [x] Basic stats collection -[ ] Build pipeline - [ ] Cross-compilation for all platforms - [ ] Binary signing - [ ] Version embedding +[x] Build pipeline + [x] Cross-compilation for all platforms (GitHub Actions) + [x] Version embedding + [ ] Binary signing (optional - future) ``` -### Phase 10.2: Backend Gateway +### Phase 10.2: Backend Gateway ✅ COMPLETED **Duration: ~2 weeks** ``` -[ ] Database models - [ ] Server, ServerGroup models - [ ] ServerMetrics, ServerCommand models - [ ] AgentSession model - [ ] Migrations +[x] Database models + [x] Server, ServerGroup models + [x] ServerMetrics, ServerCommand models + [x] AgentSession model + [x] Migrations (auto via SQLAlchemy) -[ ] Agent Gateway (WebSocket server) - [ ] SocketIO namespace for agents - [ ] Authentication handler - [ ] Session management - [ ] Command routing - [ ] Connection tracking +[x] Agent Gateway (WebSocket server) + [x] SocketIO namespace for agents (/agent) + [x] Authentication handler + [x] Session management + [x] Command routing + [x] Connection tracking -[ ] API endpoints - [ ] Server CRUD - [ ] Registration token generation - [ ] Server status endpoint +[x] API endpoints + [x] Server CRUD + [x] Registration token generation + [x] Server status endpoint + [x] Remote Docker endpoints (50+ routes) ``` -### Phase 10.3: Security Layer +### Phase 10.3: Security Layer ✅ COMPLETED **Duration: ~1-2 weeks** ``` -[ ] Security implementation - [ ] API key generation and hashing - [ ] HMAC signature validation - [ ] Session token management - [ ] Permission checking middleware - [ ] Rate limiting per agent - [ ] Audit logging +[x] Security implementation + [x] API key generation and hashing (bcrypt) + [x] HMAC-SHA256 signature validation + [x] Session token management + [x] Permission checking (scope-based) + [ ] Rate limiting per agent (future) + [x] Audit logging (ServerCommand model) -[ ] Agent security - [ ] Encrypted key storage - [ ] Command validation - [ ] Permission enforcement +[x] Agent security + [x] Encrypted key storage (AES-256-GCM) + [x] Command validation + [x] Permission enforcement ``` -### Phase 10.4: Frontend - Server Management +### Phase 10.4: Frontend - Server Management ✅ COMPLETED **Duration: ~2 weeks** ``` -[ ] Servers page - [ ] Server list view - [ ] Server cards/grid view - [ ] Add server modal - [ ] Installation instructions - [ ] Server detail page - [ ] Server settings +[x] Servers page + [x] Server list view (grid with cards) + [x] Server cards with live metrics + [x] Add server modal with permissions + [x] Installation instructions (Linux/Windows) + [x] Server detail page (tabbed interface) + [x] Server settings -[ ] Server groups - [ ] Group CRUD UI - [ ] Drag-drop organization - [ ] Group filtering +[x] Server groups + [x] Group CRUD UI (modal) + [x] Group filtering + [ ] Drag-drop organization (future) ``` -### Phase 10.5: Remote Docker Integration +### Phase 10.5: Remote Docker Integration ✅ COMPLETED **Duration: ~2 weeks** ``` -[ ] Backend: Docker command routing - [ ] Route Docker commands to agents - [ ] Handle async responses - [ ] Error handling +[x] Backend: Docker command routing + [x] Route Docker commands to agents (RemoteDockerService) + [x] Handle async responses (command results) + [x] Error handling -[ ] Frontend: Docker page updates - [ ] Server selector dropdown - [ ] Context-aware Docker UI - [ ] Real-time container updates +[x] Frontend: Docker page updates + [x] Server selector dropdown (top of page) + [x] Context-aware Docker UI (ServerContext) + [x] Real-time container updates -[ ] Agent: Full Docker support - [ ] All container operations - [ ] Image operations - [ ] Volume/network listing +[x] Agent: Full Docker support + [x] All container operations + [x] Image operations + [x] Volume/network listing ``` -### Phase 10.6: Monitoring & Metrics +### Phase 10.6: Monitoring & Metrics 🔄 IN PROGRESS **Duration: ~2 weeks** ``` -[ ] Real-time metrics - [ ] Agent metrics streaming - [ ] WebSocket to frontend - [ ] Live dashboard updates +[x] Real-time metrics + [x] Agent metrics streaming (heartbeat) + [x] WebSocket to frontend (SocketIO) + [x] Live dashboard updates [ ] Historical metrics - [ ] Metrics storage + [x] Metrics storage (ServerMetrics model) [ ] Retention policies [ ] Aggregation queries -[ ] Cross-server dashboard - [ ] Overview page - [ ] Metric comparison charts - [ ] Health grid view +[x] Cross-server dashboard + [x] Overview page (Servers.jsx) + [x] Metric comparison (server cards) + [x] Health grid view ``` -### Phase 10.7: Installation & Distribution +### Phase 10.7: Installation & Distribution 🔄 IN PROGRESS **Duration: ~1-2 weeks** ``` -[ ] Installation scripts - [ ] Linux install script (bash) - [ ] Windows install script (PowerShell) +[x] Build automation + [x] GitHub Actions workflow (agent-release.yml) + [x] Linux amd64/arm64 builds + [x] Windows amd64 builds + [x] Automatic releases on tag + +[x] Installation scripts + [x] Linux install script (bash) - created + [x] Windows install script (PowerShell) - created + [x] Installation endpoint in backend [ ] Docker image [ ] Package building @@ -1325,7 +1333,7 @@ system:exec: # Requires explicit permission [ ] Homebrew formula [ ] Distribution - [ ] Download page + [ ] Download page in UI [ ] Version checking [ ] Auto-update mechanism ``` diff --git a/backend/app/api/servers.py b/backend/app/api/servers.py index e25b35fb..4354d2d5 100644 --- a/backend/app/api/servers.py +++ b/backend/app/api/servers.py @@ -4,8 +4,9 @@ Server Management API Endpoints for managing remote servers and their agents. """ +import os from datetime import datetime, timedelta -from flask import Blueprint, request, jsonify +from flask import Blueprint, request, jsonify, Response, current_app from flask_jwt_extended import jwt_required, get_jwt_identity from app import db @@ -833,3 +834,136 @@ def get_remote_system_info(server_id): return jsonify(result), 500 return jsonify(result.get('data')) + + +# ==================== Installation Scripts ==================== + +def _get_scripts_dir(): + """Get the scripts directory path""" + # Go up from backend/app/api to backend, then to scripts + base_dir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(__file__)))) + return os.path.join(base_dir, 'scripts') + + +@servers_bp.route('/install.sh', methods=['GET']) +def get_install_script_linux(): + """ + Get the Linux installation script. + + This endpoint serves the bash installation script for installing + the ServerKit agent on Linux systems. + + Usage: + curl -fsSL https://your-server/api/servers/install.sh | sudo bash -s -- \\ + --token "YOUR_TOKEN" --server "https://your-server" + """ + script_path = os.path.join(_get_scripts_dir(), 'install.sh') + + if not os.path.exists(script_path): + return jsonify({'error': 'Installation script not found'}), 404 + + with open(script_path, 'r', encoding='utf-8') as f: + content = f.read() + + # Replace placeholder URLs with actual server URL + server_url = request.url_root.rstrip('/') + content = content.replace('https://your-serverkit.com', server_url) + + return Response( + content, + mimetype='text/x-shellscript', + headers={ + 'Content-Disposition': 'inline; filename="install.sh"', + 'Cache-Control': 'no-cache' + } + ) + + +@servers_bp.route('/install.ps1', methods=['GET']) +def get_install_script_windows(): + """ + Get the Windows installation script. + + This endpoint serves the PowerShell installation script for installing + the ServerKit agent on Windows systems. + + Usage: + irm https://your-server/api/servers/install.ps1 | iex; \\ + Install-ServerKitAgent -Token "YOUR_TOKEN" -Server "https://your-server" + """ + script_path = os.path.join(_get_scripts_dir(), 'install.ps1') + + if not os.path.exists(script_path): + return jsonify({'error': 'Installation script not found'}), 404 + + with open(script_path, 'r', encoding='utf-8') as f: + content = f.read() + + # Replace placeholder URLs with actual server URL + server_url = request.url_root.rstrip('/') + content = content.replace('https://your-serverkit.com', server_url) + + return Response( + content, + mimetype='text/plain', + headers={ + 'Content-Disposition': 'inline; filename="install.ps1"', + 'Cache-Control': 'no-cache' + } + ) + + +@servers_bp.route('/install-instructions/', methods=['GET']) +@jwt_required() +def get_install_instructions(server_id): + """ + Get installation instructions for a specific server. + + Returns the installation commands with the server's registration token + already embedded. + """ + server = Server.query.get(server_id) + if not server: + return jsonify({'error': 'Server not found'}), 404 + + # Check if server has a valid registration token + if not server.registration_token_hash: + return jsonify({ + 'error': 'No registration token available', + 'message': 'Generate a new token using the regenerate-token endpoint' + }), 400 + + if server.registration_token_expires and server.registration_token_expires < datetime.utcnow(): + return jsonify({ + 'error': 'Registration token expired', + 'message': 'Generate a new token using the regenerate-token endpoint' + }), 400 + + # Get base URL + base_url = request.url_root.rstrip('/') + api_url = f"{base_url}/api/servers" + + return jsonify({ + 'linux': { + 'one_liner': f'curl -fsSL {api_url}/install.sh | sudo bash -s -- --token "YOUR_TOKEN" --server "{base_url}"', + 'manual': [ + f'# Download the script', + f'curl -fsSL {api_url}/install.sh -o install.sh', + f'chmod +x install.sh', + f'', + f'# Run installation', + f'sudo ./install.sh --token "YOUR_TOKEN" --server "{base_url}"' + ] + }, + 'windows': { + 'one_liner': f'irm {api_url}/install.ps1 | iex; Install-ServerKitAgent -Token "YOUR_TOKEN" -Server "{base_url}"', + 'manual': [ + f'# Download the script (run in PowerShell as Administrator)', + f'Invoke-WebRequest -Uri "{api_url}/install.ps1" -OutFile install.ps1', + f'', + f'# Run installation', + f'.\\install.ps1 -Token "YOUR_TOKEN" -Server "{base_url}"' + ] + }, + 'note': 'Replace YOUR_TOKEN with the registration token shown in the UI' + }) diff --git a/scripts/install.ps1 b/scripts/install.ps1 new file mode 100644 index 00000000..160e4dce --- /dev/null +++ b/scripts/install.ps1 @@ -0,0 +1,323 @@ +#Requires -RunAsAdministrator +<# +.SYNOPSIS + ServerKit Agent Installation Script for Windows + +.DESCRIPTION + Downloads and installs the ServerKit Agent on Windows systems. + +.PARAMETER Token + Registration token from ServerKit (required) + +.PARAMETER Server + ServerKit server URL (required) + +.PARAMETER Name + Display name for this server (optional) + +.PARAMETER Version + Specific agent version to install (optional, defaults to latest) + +.EXAMPLE + .\install.ps1 -Token "sk_reg_xxx" -Server "https://your-serverkit.com" + +.EXAMPLE + irm https://your-serverkit.com/install.ps1 | iex; Install-ServerKitAgent -Token "sk_reg_xxx" -Server "https://your-serverkit.com" + +.NOTES + Requires Administrator privileges. +#> + +param( + [Parameter(Mandatory=$false)] + [string]$Token, + + [Parameter(Mandatory=$false)] + [string]$Server, + + [Parameter(Mandatory=$false)] + [string]$Name, + + [Parameter(Mandatory=$false)] + [string]$Version = "latest" +) + +# Configuration +$InstallDir = "$env:ProgramFiles\ServerKit" +$ConfigDir = "$env:ProgramData\ServerKit" +$ServiceName = "ServerKitAgent" +$GitHubRepo = "serverkit/serverkit" +$AgentBinary = "serverkit-agent.exe" + +# Colors +function Write-ColorOutput { + param( + [string]$Message, + [string]$Type = "Info" + ) + + switch ($Type) { + "Info" { Write-Host "[INFO] " -ForegroundColor Cyan -NoNewline; Write-Host $Message } + "Success" { Write-Host "[SUCCESS] " -ForegroundColor Green -NoNewline; Write-Host $Message } + "Warning" { Write-Host "[WARN] " -ForegroundColor Yellow -NoNewline; Write-Host $Message } + "Error" { Write-Host "[ERROR] " -ForegroundColor Red -NoNewline; Write-Host $Message } + } +} + +function Write-Banner { + Write-Host "" + Write-Host "╔═══════════════════════════════════════════════════════════╗" -ForegroundColor Cyan + Write-Host "║ ServerKit Agent Installer ║" -ForegroundColor Cyan + Write-Host "╚═══════════════════════════════════════════════════════════╝" -ForegroundColor Cyan + Write-Host "" +} + +function Test-Administrator { + $currentPrincipal = New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent()) + return $currentPrincipal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) +} + +function Get-LatestVersion { + if ($Version -eq "latest") { + Write-ColorOutput "Fetching latest version..." "Info" + + try { + $releases = Invoke-RestMethod -Uri "https://api.github.com/repos/$GitHubRepo/releases" -Method Get + foreach ($release in $releases) { + if ($release.tag_name -match "^agent-v(.+)$") { + $script:Version = $Matches[1] + Write-ColorOutput "Latest version: v$Version" "Info" + return + } + } + throw "No agent release found" + } + catch { + Write-ColorOutput "Failed to fetch latest version: $_" "Error" + exit 1 + } + } +} + +function Install-Agent { + Write-ColorOutput "Downloading ServerKit Agent v$Version..." "Info" + + # Create temp directory + $tempDir = Join-Path $env:TEMP "serverkit-install-$(Get-Random)" + New-Item -ItemType Directory -Path $tempDir -Force | Out-Null + + $downloadUrl = "https://github.com/$GitHubRepo/releases/download/agent-v$Version/serverkit-agent-$Version-windows-amd64.zip" + $archivePath = Join-Path $tempDir "serverkit-agent.zip" + + try { + # Download + Write-ColorOutput "Downloading from: $downloadUrl" "Info" + Invoke-WebRequest -Uri $downloadUrl -OutFile $archivePath -UseBasicParsing + + # Extract + Write-ColorOutput "Extracting agent..." "Info" + Expand-Archive -Path $archivePath -DestinationPath $tempDir -Force + + # Create install directory + if (-not (Test-Path $InstallDir)) { + New-Item -ItemType Directory -Path $InstallDir -Force | Out-Null + } + + # Copy binary + $binarySource = Join-Path $tempDir "serverkit-agent-windows-amd64.exe" + $binaryDest = Join-Path $InstallDir $AgentBinary + Copy-Item -Path $binarySource -Destination $binaryDest -Force + + Write-ColorOutput "Agent installed to $binaryDest" "Success" + } + catch { + Write-ColorOutput "Failed to download/install agent: $_" "Error" + exit 1 + } + finally { + # Cleanup + Remove-Item -Path $tempDir -Recurse -Force -ErrorAction SilentlyContinue + } +} + +function New-ConfigDirectory { + Write-ColorOutput "Creating configuration directory..." "Info" + + if (-not (Test-Path $ConfigDir)) { + New-Item -ItemType Directory -Path $ConfigDir -Force | Out-Null + } +} + +function Register-Agent { + param( + [string]$Token, + [string]$Server, + [string]$Name + ) + + Write-ColorOutput "Registering agent with ServerKit..." "Info" + + $agentPath = Join-Path $InstallDir $AgentBinary + $arguments = @("register", "--token", $Token, "--server", $Server) + + if ($Name) { + $arguments += @("--name", $Name) + } + + try { + $process = Start-Process -FilePath $agentPath -ArgumentList $arguments -Wait -PassThru -NoNewWindow + if ($process.ExitCode -ne 0) { + throw "Registration command failed with exit code: $($process.ExitCode)" + } + Write-ColorOutput "Agent registered successfully" "Success" + } + catch { + Write-ColorOutput "Agent registration failed: $_" "Error" + exit 1 + } +} + +function Install-WindowsService { + Write-ColorOutput "Installing Windows service..." "Info" + + $agentPath = Join-Path $InstallDir $AgentBinary + + # Check if service already exists + $existingService = Get-Service -Name $ServiceName -ErrorAction SilentlyContinue + + if ($existingService) { + Write-ColorOutput "Stopping existing service..." "Info" + Stop-Service -Name $ServiceName -Force -ErrorAction SilentlyContinue + Start-Sleep -Seconds 2 + + Write-ColorOutput "Removing existing service..." "Info" + sc.exe delete $ServiceName | Out-Null + Start-Sleep -Seconds 2 + } + + # Create the service using sc.exe + $binPath = "`"$agentPath`" start" + $result = sc.exe create $ServiceName binPath= $binPath start= auto DisplayName= "ServerKit Agent" + + if ($LASTEXITCODE -ne 0) { + Write-ColorOutput "Failed to create service: $result" "Error" + exit 1 + } + + # Set service description + sc.exe description $ServiceName "ServerKit Agent for remote server management" | Out-Null + + # Set service recovery options (restart on failure) + sc.exe failure $ServiceName reset= 86400 actions= restart/60000/restart/60000/restart/60000 | Out-Null + + Write-ColorOutput "Windows service installed" "Success" +} + +function Start-AgentService { + Write-ColorOutput "Starting ServerKit Agent service..." "Info" + + try { + Start-Service -Name $ServiceName + Start-Sleep -Seconds 3 + + $service = Get-Service -Name $ServiceName + if ($service.Status -eq "Running") { + Write-ColorOutput "Agent service is running" "Success" + } + else { + Write-ColorOutput "Service failed to start. Status: $($service.Status)" "Warning" + Write-ColorOutput "Check logs in Event Viewer > Windows Logs > Application" "Info" + } + } + catch { + Write-ColorOutput "Failed to start service: $_" "Error" + } +} + +function Add-ToPath { + Write-ColorOutput "Adding to system PATH..." "Info" + + $currentPath = [Environment]::GetEnvironmentVariable("Path", "Machine") + + if ($currentPath -notlike "*$InstallDir*") { + $newPath = "$currentPath;$InstallDir" + [Environment]::SetEnvironmentVariable("Path", $newPath, "Machine") + Write-ColorOutput "Added $InstallDir to system PATH" "Success" + } + else { + Write-ColorOutput "Already in PATH" "Info" + } +} + +function Write-SuccessMessage { + Write-Host "" + Write-Host "╔═══════════════════════════════════════════════════════════╗" -ForegroundColor Green + Write-Host "║ Installation completed successfully! ║" -ForegroundColor Green + Write-Host "╚═══════════════════════════════════════════════════════════╝" -ForegroundColor Green + Write-Host "" + Write-Host "Agent Status:" + Write-Host " Binary: $InstallDir\$AgentBinary" + Write-Host " Config: $ConfigDir\config.yaml" + Write-Host " Service: $ServiceName" + Write-Host "" + Write-Host "Useful commands:" + Write-Host " Check status: Get-Service $ServiceName" + Write-Host " View logs: Get-EventLog -LogName Application -Source $ServiceName" + Write-Host " Restart agent: Restart-Service $ServiceName" + Write-Host " Stop agent: Stop-Service $ServiceName" + Write-Host "" +} + +# Main function for piped execution +function Install-ServerKitAgent { + param( + [Parameter(Mandatory=$true)] + [string]$Token, + + [Parameter(Mandatory=$true)] + [string]$Server, + + [Parameter(Mandatory=$false)] + [string]$Name, + + [Parameter(Mandatory=$false)] + [string]$Version = "latest" + ) + + # Update script-level variables + $script:Version = $Version + + Write-Banner + + if (-not (Test-Administrator)) { + Write-ColorOutput "This script must be run as Administrator" "Error" + Write-Host "Right-click PowerShell and select 'Run as Administrator'" + exit 1 + } + + Get-LatestVersion + Install-Agent + New-ConfigDirectory + Register-Agent -Token $Token -Server $Server -Name $Name + Install-WindowsService + Start-AgentService + Add-ToPath + Write-SuccessMessage +} + +# Direct execution with parameters +if ($Token -and $Server) { + Install-ServerKitAgent -Token $Token -Server $Server -Name $Name -Version $Version +} +elseif ($PSBoundParameters.Count -gt 0) { + Write-ColorOutput "Both -Token and -Server parameters are required" "Error" + Write-Host "" + Write-Host "Usage:" + Write-Host " .\install.ps1 -Token 'sk_reg_xxx' -Server 'https://your-serverkit.com'" + exit 1 +} +else { + # Script was sourced, export the function + Write-Host "ServerKit Agent installer loaded." -ForegroundColor Cyan + Write-Host "Usage: Install-ServerKitAgent -Token 'sk_reg_xxx' -Server 'https://your-serverkit.com'" -ForegroundColor Gray +} diff --git a/scripts/install.sh b/scripts/install.sh new file mode 100644 index 00000000..7650eb99 --- /dev/null +++ b/scripts/install.sh @@ -0,0 +1,348 @@ +#!/bin/bash +# +# ServerKit Agent Installation Script +# +# Usage: +# curl -fsSL https://your-serverkit.com/install.sh | sudo bash -s -- --token "TOKEN" --server "URL" +# +# Options: +# --token, -t Registration token (required) +# --server, -s ServerKit server URL (required) +# --name, -n Display name for this server (optional) +# --version, -v Specific agent version to install (optional, defaults to latest) +# --help, -h Show this help message +# + +set -e + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# Configuration +INSTALL_DIR="/usr/local/bin" +CONFIG_DIR="/etc/serverkit" +SERVICE_USER="serverkit" +GITHUB_REPO="serverkit/serverkit" +AGENT_BINARY="serverkit-agent" + +# Arguments +TOKEN="" +SERVER_URL="" +SERVER_NAME="" +VERSION="latest" + +print_banner() { + echo -e "${BLUE}" + echo "╔═══════════════════════════════════════════════════════════╗" + echo "║ ServerKit Agent Installer ║" + echo "╚═══════════════════════════════════════════════════════════╝" + echo -e "${NC}" +} + +log_info() { + echo -e "${BLUE}[INFO]${NC} $1" +} + +log_success() { + echo -e "${GREEN}[SUCCESS]${NC} $1" +} + +log_warn() { + echo -e "${YELLOW}[WARN]${NC} $1" +} + +log_error() { + echo -e "${RED}[ERROR]${NC} $1" + exit 1 +} + +show_help() { + echo "ServerKit Agent Installation Script" + echo "" + echo "Usage: install.sh [OPTIONS]" + echo "" + echo "Options:" + echo " --token, -t Registration token (required)" + echo " --server, -s ServerKit server URL (required)" + echo " --name, -n Display name for this server (optional)" + echo " --version, -v Specific agent version (optional, defaults to latest)" + echo " --help, -h Show this help message" + echo "" + echo "Example:" + echo " curl -fsSL https://your-serverkit.com/install.sh | sudo bash -s -- \\" + echo " --token 'sk_reg_xxx' \\" + echo " --server 'https://your-serverkit.com'" + exit 0 +} + +parse_args() { + while [[ $# -gt 0 ]]; do + case $1 in + --token|-t) + TOKEN="$2" + shift 2 + ;; + --server|-s) + SERVER_URL="$2" + shift 2 + ;; + --name|-n) + SERVER_NAME="$2" + shift 2 + ;; + --version|-v) + VERSION="$2" + shift 2 + ;; + --help|-h) + show_help + ;; + *) + log_error "Unknown option: $1" + ;; + esac + done + + # Validate required arguments + if [[ -z "$TOKEN" ]]; then + log_error "Registration token is required. Use --token or -t" + fi + + if [[ -z "$SERVER_URL" ]]; then + log_error "Server URL is required. Use --server or -s" + fi +} + +check_root() { + if [[ $EUID -ne 0 ]]; then + log_error "This script must be run as root (use sudo)" + fi +} + +detect_platform() { + OS=$(uname -s | tr '[:upper:]' '[:lower:]') + ARCH=$(uname -m) + + case "$ARCH" in + x86_64|amd64) + ARCH="amd64" + ;; + aarch64|arm64) + ARCH="arm64" + ;; + *) + log_error "Unsupported architecture: $ARCH" + ;; + esac + + if [[ "$OS" != "linux" ]]; then + log_error "This script only supports Linux. For Windows, use install.ps1" + fi + + log_info "Detected platform: ${OS}-${ARCH}" +} + +check_dependencies() { + log_info "Checking dependencies..." + + # Check for required tools + for cmd in curl tar; do + if ! command -v "$cmd" &> /dev/null; then + log_error "$cmd is required but not installed" + fi + done + + # Check for systemd + if ! command -v systemctl &> /dev/null; then + log_warn "systemd not found. You will need to start the agent manually." + fi +} + +get_latest_version() { + if [[ "$VERSION" == "latest" ]]; then + log_info "Fetching latest version..." + VERSION=$(curl -fsSL "https://api.github.com/repos/${GITHUB_REPO}/releases" | \ + grep -oP '"tag_name": "agent-v\K[^"]+' | head -1) + + if [[ -z "$VERSION" ]]; then + log_error "Failed to fetch latest version" + fi + log_info "Latest version: v${VERSION}" + fi +} + +download_agent() { + log_info "Downloading ServerKit Agent v${VERSION}..." + + DOWNLOAD_URL="https://github.com/${GITHUB_REPO}/releases/download/agent-v${VERSION}/serverkit-agent-${VERSION}-linux-${ARCH}.tar.gz" + TMP_DIR=$(mktemp -d) + ARCHIVE="${TMP_DIR}/serverkit-agent.tar.gz" + + if ! curl -fsSL "$DOWNLOAD_URL" -o "$ARCHIVE"; then + rm -rf "$TMP_DIR" + log_error "Failed to download agent from: $DOWNLOAD_URL" + fi + + # Extract binary + log_info "Extracting agent..." + tar -xzf "$ARCHIVE" -C "$TMP_DIR" + + # Install binary + mv "${TMP_DIR}/serverkit-agent-linux-${ARCH}" "${INSTALL_DIR}/${AGENT_BINARY}" + chmod +x "${INSTALL_DIR}/${AGENT_BINARY}" + + # Cleanup + rm -rf "$TMP_DIR" + + log_success "Agent installed to ${INSTALL_DIR}/${AGENT_BINARY}" +} + +create_user() { + if id "$SERVICE_USER" &>/dev/null; then + log_info "User $SERVICE_USER already exists" + else + log_info "Creating service user: $SERVICE_USER" + useradd -r -s /bin/false -d /nonexistent "$SERVICE_USER" + fi + + # Add to docker group if it exists + if getent group docker > /dev/null 2>&1; then + usermod -aG docker "$SERVICE_USER" + log_info "Added $SERVICE_USER to docker group" + fi +} + +create_config_dir() { + log_info "Creating configuration directory..." + mkdir -p "$CONFIG_DIR" + chown "$SERVICE_USER:$SERVICE_USER" "$CONFIG_DIR" + chmod 750 "$CONFIG_DIR" +} + +register_agent() { + log_info "Registering agent with ServerKit..." + + REGISTER_CMD="${INSTALL_DIR}/${AGENT_BINARY} register --token \"${TOKEN}\" --server \"${SERVER_URL}\"" + + if [[ -n "$SERVER_NAME" ]]; then + REGISTER_CMD="${REGISTER_CMD} --name \"${SERVER_NAME}\"" + fi + + if ! eval "$REGISTER_CMD"; then + log_error "Agent registration failed" + fi + + # Fix permissions on config files + chown -R "$SERVICE_USER:$SERVICE_USER" "$CONFIG_DIR" + + log_success "Agent registered successfully" +} + +install_systemd_service() { + if ! command -v systemctl &> /dev/null; then + log_warn "Skipping systemd service installation (systemd not found)" + return + fi + + log_info "Installing systemd service..." + + cat > /etc/systemd/system/serverkit-agent.service << EOF +[Unit] +Description=ServerKit Agent +Documentation=https://github.com/${GITHUB_REPO} +After=network-online.target docker.service +Wants=network-online.target + +[Service] +Type=simple +User=${SERVICE_USER} +Group=${SERVICE_USER} +ExecStart=${INSTALL_DIR}/${AGENT_BINARY} start +Restart=always +RestartSec=10 +StandardOutput=journal +StandardError=journal +SyslogIdentifier=serverkit-agent + +# Security hardening +NoNewPrivileges=true +ProtectSystem=strict +ProtectHome=true +ReadWritePaths=${CONFIG_DIR} +PrivateTmp=true + +# Environment +Environment=HOME=/nonexistent + +[Install] +WantedBy=multi-user.target +EOF + + systemctl daemon-reload + systemctl enable serverkit-agent + + log_success "Systemd service installed" +} + +start_service() { + if ! command -v systemctl &> /dev/null; then + log_warn "Cannot auto-start without systemd" + echo "" + echo "To start the agent manually:" + echo " ${INSTALL_DIR}/${AGENT_BINARY} start" + return + fi + + log_info "Starting ServerKit Agent..." + systemctl start serverkit-agent + + sleep 2 + + if systemctl is-active --quiet serverkit-agent; then + log_success "Agent is running" + else + log_error "Failed to start agent. Check logs with: journalctl -u serverkit-agent" + fi +} + +print_success() { + echo "" + echo -e "${GREEN}╔═══════════════════════════════════════════════════════════╗${NC}" + echo -e "${GREEN}║ Installation completed successfully! ║${NC}" + echo -e "${GREEN}╚═══════════════════════════════════════════════════════════╝${NC}" + echo "" + echo "Agent Status:" + echo " Binary: ${INSTALL_DIR}/${AGENT_BINARY}" + echo " Config: ${CONFIG_DIR}/config.yaml" + echo " Service: serverkit-agent" + echo "" + echo "Useful commands:" + echo " Check status: systemctl status serverkit-agent" + echo " View logs: journalctl -u serverkit-agent -f" + echo " Restart agent: systemctl restart serverkit-agent" + echo " Stop agent: systemctl stop serverkit-agent" + echo "" +} + +# Main execution +main() { + print_banner + parse_args "$@" + check_root + detect_platform + check_dependencies + get_latest_version + download_agent + create_user + create_config_dir + register_agent + install_systemd_service + start_service + print_success +} + +main "$@"