125 lines
6.0 KiB
PowerShell
125 lines
6.0 KiB
PowerShell
[CmdletBinding()]
|
|
param(
|
|
[string]$RegistrationToken,
|
|
|
|
[string]$InstanceUrl = "https://gitea.officecom.cloud",
|
|
[string]$RunnerName = "officecom-oc-sentinel-windows-01",
|
|
[string]$RunnerVersion = "1.0.8",
|
|
[string]$RunnerAccount = "OCGiteaRunner",
|
|
[string]$InstallRoot = "$env:ProgramData\\OCGiteaRunner"
|
|
)
|
|
|
|
$ErrorActionPreference = "Stop"
|
|
|
|
function Test-IsAdministrator {
|
|
$identity = [Security.Principal.WindowsIdentity]::GetCurrent()
|
|
$principal = New-Object Security.Principal.WindowsPrincipal($identity)
|
|
return $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
|
|
}
|
|
|
|
function New-RunnerPassword {
|
|
# The account is only used by Task Scheduler; no password is persisted in this script or repository.
|
|
$bytes = New-Object byte[] 36
|
|
[Security.Cryptography.RandomNumberGenerator]::Create().GetBytes($bytes)
|
|
return [Convert]::ToBase64String($bytes).Replace('+', 'A').Replace('/', 'B').Replace('=', 'C') + "!9z"
|
|
}
|
|
|
|
if (-not (Test-IsAdministrator)) {
|
|
throw "Run this script from an elevated PowerShell window (Run as administrator)."
|
|
}
|
|
|
|
if ([string]::IsNullOrWhiteSpace($RegistrationToken)) {
|
|
$secureToken = Read-Host "Paste the repository runner registration token" -AsSecureString
|
|
$tokenPointer = [Runtime.InteropServices.Marshal]::SecureStringToBSTR($secureToken)
|
|
try {
|
|
$RegistrationToken = [Runtime.InteropServices.Marshal]::PtrToStringBSTR($tokenPointer)
|
|
}
|
|
finally {
|
|
[Runtime.InteropServices.Marshal]::ZeroFreeBSTR($tokenPointer)
|
|
}
|
|
}
|
|
|
|
if ([string]::IsNullOrWhiteSpace($RegistrationToken)) {
|
|
throw "A repository runner registration token is required."
|
|
}
|
|
|
|
$taskName = "OCSentinel Gitea Windows Runner"
|
|
$runnerPath = Join-Path $InstallRoot "gitea-runner.exe"
|
|
$configPath = Join-Path $InstallRoot "config.yaml"
|
|
$runnerStatePath = Join-Path $InstallRoot ".runner"
|
|
$logDirectory = Join-Path $InstallRoot "logs"
|
|
$downloadUrl = "https://gitea.com/gitea/act_runner/releases/download/v$RunnerVersion/gitea-runner-$RunnerVersion-windows-amd64.exe"
|
|
$checksumUrl = "$downloadUrl.sha256"
|
|
$accountQualifiedName = "$env:COMPUTERNAME\\$RunnerAccount"
|
|
|
|
if (Test-Path -LiteralPath $runnerStatePath) {
|
|
throw "A runner is already registered at $InstallRoot. Remove it in Gitea first, then remove this directory if a new registration is needed."
|
|
}
|
|
|
|
$existingAccount = Get-LocalUser -Name $RunnerAccount -ErrorAction SilentlyContinue
|
|
if ($existingAccount) {
|
|
throw "The local account '$RunnerAccount' already exists. Stop and remove the existing runner before reinstalling it."
|
|
}
|
|
|
|
New-Item -ItemType Directory -Force -Path $InstallRoot, $logDirectory | Out-Null
|
|
|
|
try {
|
|
Write-Host "Downloading Gitea runner $RunnerVersion..."
|
|
Invoke-WebRequest -UseBasicParsing -Uri $downloadUrl -OutFile $runnerPath
|
|
$checksumText = (Invoke-WebRequest -UseBasicParsing -Uri $checksumUrl).Content.Trim()
|
|
$expectedHash = ($checksumText -split '\s+')[0].ToLowerInvariant()
|
|
$actualHash = (Get-FileHash -LiteralPath $runnerPath -Algorithm SHA256).Hash.ToLowerInvariant()
|
|
if ($actualHash -ne $expectedHash) {
|
|
throw "Runner checksum verification failed."
|
|
}
|
|
|
|
$password = New-RunnerPassword
|
|
$securePassword = ConvertTo-SecureString -String $password -AsPlainText -Force
|
|
New-LocalUser -Name $RunnerAccount -Password $securePassword -Description "Restricted account for the OCSentinel Gitea Actions runner." -AccountNeverExpires | Out-Null
|
|
|
|
$config = & $runnerPath generate-config
|
|
$config = $config -replace '(?m)^ labels:.*$', ' labels: ["windows:host"]'
|
|
Set-Content -LiteralPath $configPath -Value $config -Encoding utf8
|
|
|
|
# Build jobs run only with this non-administrative account and only for the repository runner token supplied.
|
|
$acl = Get-Acl -LiteralPath $InstallRoot
|
|
$acl.SetAccessRuleProtection($true, $false)
|
|
$systemRule = New-Object Security.AccessControl.FileSystemAccessRule("SYSTEM", "FullControl", "ContainerInherit,ObjectInherit", "None", "Allow")
|
|
$adminRule = New-Object Security.AccessControl.FileSystemAccessRule("BUILTIN\\Administrators", "FullControl", "ContainerInherit,ObjectInherit", "None", "Allow")
|
|
$runnerRule = New-Object Security.AccessControl.FileSystemAccessRule($accountQualifiedName, "Modify", "ContainerInherit,ObjectInherit", "None", "Allow")
|
|
$acl.AddAccessRule($systemRule)
|
|
$acl.AddAccessRule($adminRule)
|
|
$acl.AddAccessRule($runnerRule)
|
|
Set-Acl -LiteralPath $InstallRoot -AclObject $acl
|
|
|
|
$credential = New-Object Management.Automation.PSCredential($accountQualifiedName, $securePassword)
|
|
$registerArgs = @(
|
|
"--config", "`"$configPath`"", "register", "--no-interactive",
|
|
"--instance", "`"$InstanceUrl`"", "--token", "`"$RegistrationToken`"",
|
|
"--name", "`"$RunnerName`"", "--labels", "windows:host"
|
|
) -join " "
|
|
$registration = Start-Process -FilePath $runnerPath -ArgumentList $registerArgs -WorkingDirectory $InstallRoot -Credential $credential -Wait -PassThru
|
|
if ($registration.ExitCode -ne 0 -or -not (Test-Path -LiteralPath $runnerStatePath)) {
|
|
throw "Runner registration failed with exit code $($registration.ExitCode)."
|
|
}
|
|
|
|
$action = New-ScheduledTaskAction -Execute $runnerPath -Argument "--config `"$configPath`" daemon" -WorkingDirectory $InstallRoot
|
|
$trigger = New-ScheduledTaskTrigger -AtStartup
|
|
Register-ScheduledTask -TaskName $taskName -Action $action -Trigger $trigger -User $accountQualifiedName -Password $password -RunLevel Limited -Description "Runs the repository-scoped OCSentinel Gitea Actions runner." -Force | Out-Null
|
|
Start-ScheduledTask -TaskName $taskName
|
|
|
|
Start-Sleep -Seconds 3
|
|
$task = Get-ScheduledTask -TaskName $taskName
|
|
Write-Host "Gitea runner installed successfully."
|
|
Write-Host "Runner: $RunnerName"
|
|
Write-Host "Labels: windows:host"
|
|
Write-Host "Task: $taskName ($($task.State))"
|
|
Write-Host "Install path: $InstallRoot"
|
|
}
|
|
catch {
|
|
if (Get-ScheduledTask -TaskName $taskName -ErrorAction SilentlyContinue) {
|
|
Unregister-ScheduledTask -TaskName $taskName -Confirm:$false
|
|
}
|
|
throw
|
|
}
|