diff --git a/scripts/install.ps1 b/scripts/install.ps1 index 4f3e219b..645fc563 100644 --- a/scripts/install.ps1 +++ b/scripts/install.ps1 @@ -30,6 +30,17 @@ .PARAMETER Version Specific agent version to install (optional, defaults to latest) +.PARAMETER Level + Permission level: monitor (report-only, no inbound commands), protect + (security actions only) or full (everything). If omitted: a menu on an + interactive console, otherwise full with a warning. + +.PARAMETER Reconfigure + Required to change the level of an already-installed agent. + +.PARAMETER Yes + Skip the typed confirmation when reconfiguring. + .EXAMPLE .\install.ps1 -Token "sk_reg_xxx" -Server "https://your-serverkit.com" @@ -53,7 +64,15 @@ param( [string]$Name, [Parameter(Mandatory=$false)] - [string]$Version = "latest" + [string]$Version = "latest", + + [Parameter(Mandatory=$false)] + [ValidateSet("monitor", "protect", "full")] + [string]$Level, + + [switch]$Reconfigure, + + [switch]$Yes ) # Configuration @@ -172,11 +191,61 @@ function New-ConfigDirectory { } } +# Level recorded in an existing config ("" when none; no key = legacy = full). +function Get-InstalledLevel { + $cfg = Join-Path $ConfigDir "config.yaml" + if (-not (Test-Path $cfg)) { return "" } + $m = Select-String -Path $cfg -Pattern '^\s+level:\s*"?(\w+)"?' | Select-Object -First 1 + if ($m) { return $m.Matches[0].Groups[1].Value } + return "full" +} + +# Refuses to silently change an installed agent's level; returns the level to use. +function Resolve-Level { + param([string]$Level, [bool]$Reconfigure, [bool]$Yes) + + $current = Get-InstalledLevel + if ($current) { + if (-not $Level) { return $current } + if ($Level -eq $current) { return $Level } + if (-not $Reconfigure) { + Write-ColorOutput "This agent is already installed at level '$current'. Re-run with -Reconfigure to change it to '$Level'." "Error" + exit 1 + } + if (-not $Yes) { + $answer = Read-Host "Changing level $current -> $Level. Type '$Level' to confirm" + if ($answer -ne $Level) { + Write-ColorOutput "Confirmation did not match; permission level unchanged (use -Yes for scripted use)" "Error" + exit 1 + } + } + $script:ChangeLevelFrom = $current + return $Level + } + if ($Level) { return $Level } + if ([Environment]::UserInteractive -and -not [Console]::IsInputRedirected) { + Write-Host "" + Write-Host " Choose what this agent may do on this server:" + Write-Host " 1) monitor report-only, accepts no commands from the panel" + Write-Host " 2) protect security actions only (firewall, fail2ban, CrowdSec, origin lock)" + Write-Host " 3) full everything" + switch (Read-Host " Selection [1-3]") { + "1" { return "monitor" } + "2" { return "protect" } + "3" { return "full" } + default { Write-ColorOutput "Invalid selection" "Error"; exit 1 } + } + } + Write-ColorOutput "No -Level given and no console: defaulting to FULL control. Pass -Level monitor|protect|full to choose." "Warning" + return "full" +} + function Register-Agent { param( [string]$Token, [string]$Server, - [string]$Name + [string]$Name, + [string]$Level ) Write-ColorOutput "Registering agent with ServerKit..." "Info" @@ -188,6 +257,11 @@ function Register-Agent { if ($Name) { $arguments += @("--name", $Name) } + $arguments += @("--level", $Level) + + if ($script:ChangeLevelFrom) { + Start-Process -FilePath $agentPath -ArgumentList @("--config", $configPath, "permissions", "set", $Level, "--yes") -Wait -NoNewWindow | Out-Null + } try { $process = Start-Process -FilePath $agentPath -ArgumentList $arguments -Wait -PassThru -NoNewWindow @@ -307,7 +381,15 @@ function Install-ServerKitAgent { [string]$Name, [Parameter(Mandatory=$false)] - [string]$Version = "latest" + [string]$Version = "latest", + + [Parameter(Mandatory=$false)] + [ValidateSet("monitor", "protect", "full")] + [string]$Level, + + [switch]$Reconfigure, + + [switch]$Yes ) # Update script-level variables @@ -321,19 +403,30 @@ function Install-ServerKitAgent { exit 1 } + $Level = Resolve-Level -Level $Level -Reconfigure $Reconfigure.IsPresent -Yes $Yes.IsPresent + Get-LatestVersion Install-Agent New-ConfigDirectory - Register-Agent -Token $Token -Server $Server -Name $Name + Register-Agent -Token $Token -Server $Server -Name $Name -Level $Level Install-WindowsService Start-AgentService Add-ToPath Write-SuccessMessage + Write-Host "Permission level: $($Level.ToUpper())" -ForegroundColor Yellow + switch ($Level) { + "monitor" { Write-Host " Report-only: this agent sends metrics out and accepts NO commands." } + "protect" { Write-Host " Security actions only (firewall, fail2ban, CrowdSec, origin lock)." } + default { Write-Host " Full control: the panel can run any supported action." } + } + Write-Host " Change it (elevated prompt, this machine only):" + Write-Host " & '$InstallDir\$AgentBinary' permissions set " + Write-Host "" } # Direct execution with parameters if ($Token -and $Server) { - Install-ServerKitAgent -Token $Token -Server $Server -Name $Name -Version $Version + Install-ServerKitAgent -Token $Token -Server $Server -Name $Name -Version $Version -Level $Level -Reconfigure:$Reconfigure -Yes:$Yes } elseif ($PSBoundParameters.Count -gt 0) { Write-ColorOutput "Both -Token and -Server parameters are required" "Error" diff --git a/scripts/install.sh b/scripts/install.sh old mode 100644 new mode 100755 index 8eb38928..64347495 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -30,8 +30,16 @@ # --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) +# --level, -l Permission level: monitor | protect | full (see below) +# --reconfigure Allow changing the level of an already-installed agent +# --yes, -y Skip the typed confirmation when reconfiguring # --help, -h Show this help message # +# Permission levels: monitor = report-only (no inbound commands), protect = +# security actions only (firewall, fail2ban, CrowdSec, Cloudflare origin lock), +# full = everything. Omitted: a menu on a terminal; `full` (with a warning) +# when non-interactive, for compatibility with older install commands. +# set -e @@ -67,6 +75,9 @@ TOKEN="" SERVER_URL="" SERVER_NAME="" VERSION="latest" +LEVEL="" +RECONFIGURE=false +ASSUME_YES=false # Version injected by the panel when it serves this script (the panel already # resolves the latest agent release for its update endpoints). Empty means @@ -108,6 +119,9 @@ show_help() { 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 " --level, -l Permission level: monitor | protect | full (optional)" + echo " --reconfigure Allow changing the level of an already-installed agent" + echo " --yes, -y Skip the typed confirmation when reconfiguring" echo " --help, -h Show this help message" echo "" echo "Example:" @@ -142,6 +156,19 @@ parse_args() { VERSION="$2" shift 2 ;; + --level|-l) + [[ $# -ge 2 ]] || log_error "Option $1 requires a value" + LEVEL="$(printf '%s' "$2" | tr '[:upper:]' '[:lower:]')" + shift 2 + ;; + --reconfigure) + RECONFIGURE=true + shift + ;; + --yes|-y) + ASSUME_YES=true + shift + ;; --help|-h) show_help ;; @@ -159,6 +186,72 @@ parse_args() { if [[ -z "$SERVER_URL" ]]; then log_error "Server URL is required. Use --server or -s" fi + + if [[ -n "$LEVEL" ]]; then + case "$LEVEL" in + monitor|protect|full) ;; + *) log_error "Unknown permission level '$LEVEL' (use monitor, protect or full)" ;; + esac + fi +} + +# Level recorded in an existing config ("" when none; a config without the +# key is a legacy install, which means full). +installed_level() { + local cfg="${CONFIG_DIR}/config.yaml" + [[ -f "$cfg" ]] || return 0 + local lvl + lvl="$(sed -n '/^permissions:/,/^[^ ]/ s/^[[:space:]]*level:[[:space:]]*//p' "$cfg" | head -n1 | tr -d "\"' \r")" + printf '%s' "${lvl:-full}" +} + +is_tty() { [[ -t 0 && -t 1 ]]; } + +# Refuse to silently change the level of an installed agent; --reconfigure +# plus a typed confirmation (or --yes) is required. +check_reconfigure() { + local current + current="$(installed_level)" + [[ -n "$current" ]] || return 0 + if [[ -z "$LEVEL" ]]; then + LEVEL="$current" + return 0 + fi + [[ "$LEVEL" != "$current" ]] || return 0 + if [[ "$RECONFIGURE" != true ]]; then + log_error "This agent is already installed at level '$current'. Re-run with --reconfigure to change it to '$LEVEL'." + fi + if [[ "$ASSUME_YES" != true ]]; then + local answer="" + if is_tty; then + read -r -p " Changing level ${current} -> ${LEVEL}. Type '${LEVEL}' to confirm: " answer + fi + [[ "$answer" == "$LEVEL" ]] || log_error "Confirmation did not match; permission level unchanged (use --yes for scripted use)" + fi + CHANGE_LEVEL_FROM="$current" +} + +# Pick a level when none was given: menu on a terminal, else full + warning. +resolve_level() { + [[ -z "$LEVEL" ]] || return 0 + if is_tty; then + echo "" + echo " Choose what this agent may do on this server:" + echo " 1) monitor report-only, accepts no commands from the panel" + echo " 2) protect security actions only (firewall, fail2ban, CrowdSec, origin lock)" + echo " 3) full everything (docker, files, terminal, packages, ...)" + local choice="" + read -r -p " Selection [1-3]: " choice + case "$choice" in + 1|monitor) LEVEL=monitor ;; + 2|protect) LEVEL=protect ;; + 3|full) LEVEL=full ;; + *) log_error "Invalid selection" ;; + esac + else + log_warn "No --level given and no terminal: defaulting to FULL control. Pass --level monitor|protect|full to choose." + LEVEL=full + fi } check_root() { @@ -351,6 +444,14 @@ register_agent() { if [[ -n "$SERVER_NAME" ]]; then REGISTER_CMD+=(--name "${SERVER_NAME}") fi + REGISTER_CMD+=(--level "${LEVEL}") + + # Re-installing at a different level: apply it locally first so the + # register step sees a consistent config. + if [[ -n "${CHANGE_LEVEL_FROM:-}" ]]; then + "${INSTALL_DIR}/${AGENT_BINARY}" --config "${CONFIG_DIR}/config.yaml" \ + permissions set "${LEVEL}" --yes >/dev/null 2>&1 || true + fi if ! "${REGISTER_CMD[@]}"; then log_error "Agent registration failed" @@ -448,6 +549,15 @@ print_success() { echo -e "${GREEN}║ Installation completed successfully! ║${NC}" echo -e "${GREEN}╚═══════════════════════════════════════════════════════════╝${NC}" echo "" + echo -e "${YELLOW}Permission level: ${LEVEL^^}${NC}" + case "$LEVEL" in + monitor) echo " Report-only: this agent sends metrics out and accepts NO commands." ;; + protect) echo " Security actions only (firewall, fail2ban, CrowdSec, origin lock)." ;; + *) echo " Full control: the panel can run any supported action." ;; + esac + echo " Change it (as root, on this machine only):" + echo " ${INSTALL_DIR}/${AGENT_BINARY} permissions set " + echo "" echo "Agent Status:" echo " Binary: ${INSTALL_DIR}/${AGENT_BINARY}" echo " Config: ${CONFIG_DIR}/config.yaml" @@ -466,6 +576,8 @@ main() { print_banner parse_args "$@" check_root + check_reconfigure + resolve_level detect_platform check_dependencies get_latest_version