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 "$@"