gost/install-service.ps1

239 lines
8.0 KiB
PowerShell

#Requires -RunAsAdministrator
<#
.SYNOPSIS
Builds gost from source and installs it as a Windows service.
.DESCRIPTION
Runs "go build" against the local source tree, copies the resulting binary
to a target directory, and registers it as a Windows service using the
native Windows Service Control Manager. gost is built with go-svc and
runs as a proper Windows service without any wrapper.
.PARAMETER InstallDir
Directory where gost.exe and gost.yml are placed.
Default: C:\Program Files\gost
.PARAMETER ConfigFile
Path to an existing gost config file to use. If omitted and no config
exists in InstallDir, a minimal placeholder is created.
.PARAMETER ServiceName
Windows service name. Default: gost
.PARAMETER DisplayName
Windows service display name. Default: GOST Tunnel
.PARAMETER ExtraArgs
Additional arguments passed to gost.exe, e.g. "-L :8080 -D".
The -C flag pointing to the config file is always added automatically.
.PARAMETER StartupType
Service start type: Automatic, Manual, or Disabled. Default: Automatic
.PARAMETER Start
Start the service immediately after installation.
.EXAMPLE
# Build, install with defaults, and start immediately
.\install-service.ps1 -Start
.EXAMPLE
# Install with a custom config
.\install-service.ps1 -ConfigFile C:\etc\gost.yml -Start
.EXAMPLE
# Install with inline service definition (no config file)
.\install-service.ps1 -ExtraArgs "-L socks5://:1080 -L http://:8080" -Start
#>
[CmdletBinding(SupportsShouldProcess)]
param(
[string]$InstallDir = "C:\Program Files\gost",
[string]$ConfigFile = "",
[string]$ServiceName = "gost",
[string]$DisplayName = "GOST Tunnel",
[string]$ExtraArgs = "",
[ValidateSet("Automatic","Manual","Disabled")]
[string]$StartupType = "Automatic",
[switch]$Start
)
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
# Directory containing this script == repo root
$RepoRoot = $PSScriptRoot
# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------
function Write-Step([string]$msg) { Write-Host "`n==> $msg" -ForegroundColor Cyan }
function Write-Ok([string]$msg) { Write-Host " OK $msg" -ForegroundColor Green }
function Write-Warn([string]$msg) { Write-Host " WARN $msg" -ForegroundColor Yellow }
function Build-Binary([string]$destDir) {
if (-not (Get-Command go -ErrorAction SilentlyContinue)) {
throw "go not found in PATH. Please install Go from https://go.dev/dl/"
}
Write-Step "Building gost from source ($RepoRoot)..."
if (-not (Test-Path $destDir)) {
New-Item -ItemType Directory -Path $destDir -Force | Out-Null
}
$exeDest = Join-Path $destDir "gost.exe"
# Embed version: prefer git tag, fall back to version.go
$ldflags = "-s -w"
$version = $null
if (Get-Command git -ErrorAction SilentlyContinue) {
$version = git -C $RepoRoot describe --tags --abbrev=0 2>$null
}
if (-not $version) {
$verFile = Join-Path $RepoRoot "cmd\gost\version.go"
if (Test-Path $verFile) {
$match = Select-String -Path $verFile -Pattern 'version\s*=\s*"([^"]+)"'
if ($match) { $version = $match.Matches[0].Groups[1].Value }
}
}
if ($version) { $ldflags = "-s -w -X 'main.version=$version'" }
$goArgs = @("build", "-ldflags", $ldflags, "-o", $exeDest, "./cmd/gost")
Write-Host " go $($goArgs -join ' ')" -ForegroundColor DarkGray
& go @goArgs 2>&1 | ForEach-Object { Write-Host " $_" -ForegroundColor DarkGray }
if ($LASTEXITCODE -ne 0) { throw "go build failed (exit $LASTEXITCODE)" }
Write-Ok "Built: $exeDest"
return $exeDest
}
function Ensure-Config([string]$installDir, [string]$userConfig) {
$dest = Join-Path $installDir "gost.yml"
if ($userConfig -ne "") {
if (-not (Test-Path $userConfig)) {
throw "Config file not found: $userConfig"
}
$resolvedSrc = (Resolve-Path $userConfig).Path
$resolvedDest = if (Test-Path $dest) { (Resolve-Path $dest).Path } else { "" }
if ($resolvedSrc -ne $resolvedDest) {
Copy-Item $userConfig $dest -Force
Write-Ok "Config copied from $userConfig"
}
return $dest
}
if (Test-Path $dest) {
Write-Ok "Using existing config: $dest"
return $dest
}
# Create a minimal placeholder config
$placeholder = @"
# gost configuration file
# Documentation: https://gost.run/
#
# Example: HTTP proxy on port 8080
# services:
# - name: http-proxy
# addr: ":8080"
# handler:
# type: http
# listener:
# type: tcp
log:
level: info
format: json
"@
Set-Content -Path $dest -Value $placeholder -Encoding UTF8
Write-Warn "A placeholder config was created at $dest"
Write-Warn "Edit it before starting the service, or pass -ExtraArgs with -L/-F flags."
return $dest
}
function Register-GostService([string]$exePath, [string]$cfgPath, [string]$extraArgs) {
$binPath = "`"$exePath`" -C `"$cfgPath`""
if ($extraArgs -ne "") { $binPath += " $extraArgs" }
$existing = Get-Service -Name $ServiceName -ErrorAction SilentlyContinue
if ($existing) {
Write-Step "Service '$ServiceName' already exists — updating..."
if ($existing.Status -eq "Running") {
Write-Step "Stopping existing service..."
Stop-Service -Name $ServiceName -Force
$existing.WaitForStatus("Stopped", [TimeSpan]::FromSeconds(30))
}
sc.exe config $ServiceName binPath= $binPath | Out-Null
$startValue = switch ($StartupType) {
"Automatic" { "auto" }
"Manual" { "demand" }
"Disabled" { "disabled" }
}
sc.exe config $ServiceName start= $startValue | Out-Null
Write-Ok "Service updated."
} else {
Write-Step "Registering service '$ServiceName'..."
$startValue = switch ($StartupType) {
"Automatic" { "auto" }
"Manual" { "demand" }
"Disabled" { "disabled" }
}
sc.exe create $ServiceName `
binPath= $binPath `
DisplayName= $DisplayName `
start= $startValue | Out-Null
# Restart on failure: 5 s / 10 s / 30 s, reset counter after 1 day
sc.exe failure $ServiceName reset= 86400 actions= restart/5000/restart/10000/restart/30000 | Out-Null
Write-Ok "Service registered."
}
sc.exe description $ServiceName "GOST (GO Simple Tunnel) - secure tunnel service" | Out-Null
}
# ---------------------------------------------------------------------------
# Main
# ---------------------------------------------------------------------------
Write-Host ""
Write-Host " gost Windows Service Installer (build from source)" -ForegroundColor White
Write-Host " ===================================================" -ForegroundColor White
Write-Host " Repo : $RepoRoot"
Write-Host " Install dir: $InstallDir"
Write-Host " Service : $ServiceName ($StartupType)"
Write-Host ""
# 1. Build binary from source
$exePath = Build-Binary $InstallDir
# 2. Ensure config exists
$cfgPath = Ensure-Config $InstallDir $ConfigFile
# 3. Register Windows service
Register-GostService $exePath $cfgPath $ExtraArgs
# 4. Optionally start
if ($Start) {
Write-Step "Starting service '$ServiceName'..."
Start-Service -Name $ServiceName
$svc = Get-Service -Name $ServiceName
$svc.WaitForStatus("Running", [TimeSpan]::FromSeconds(15))
Write-Ok "Service is running."
}
Write-Host ""
Write-Host " Done!" -ForegroundColor Green
Write-Host ""
Write-Host " Useful commands:" -ForegroundColor White
Write-Host " Start : Start-Service $ServiceName"
Write-Host " Stop : Stop-Service $ServiceName"
Write-Host " Status : Get-Service $ServiceName"
Write-Host " Logs : Get-EventLog -LogName Application -Source $ServiceName -Newest 20"
Write-Host " Uninstall: sc.exe delete $ServiceName"
Write-Host ""