Public/Invoke-SetupAtomicRunner.ps1
function Invoke-SetupAtomicRunner { # ensure running with admin privs if ($artConfig.OS -eq "windows") { # auto-elevate on Windows $currentUser = New-Object Security.Principal.WindowsPrincipal $([Security.Principal.WindowsIdentity]::GetCurrent()) $testadmin = $currentUser.IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator) if ($testadmin -eq $false) { Start-Process powershell.exe -Verb RunAs -ArgumentList ('-noprofile -noexit -file "{0}" -elevated' -f ($myinvocation.MyCommand.Definition)) exit $LASTEXITCODE } } else { # linux and macos check - doesn't auto-elevate if ((id -u) -ne 0 ) { Throw "You must run the Invoke-SetupAtomicRunner script as root" exit } } if ($artConfig.basehostname.length -gt 15) { Throw "The hostname for this machine (minus the GUID) must be 15 characters or less. Please rename this computer." } #create AtomicRunner-Logs directories if they don't exist New-Item -ItemType Directory $artConfig.atomicLogsPath -ErrorAction Ignore New-Item -ItemType Directory $artConfig.runnerFolder -ErrorAction Ignore if ($artConfig.gmsaAccount) { Start-Service WinRM $path = Join-Path $env:ProgramFiles "WindowsPowerShell\Modules\RenameRunner\RoleCapabilities" New-Item -ItemType Directory $path -ErrorAction Ignore New-PSSessionConfigurationFile -SessionType RestrictedRemoteServer -GroupManagedServiceAccount $artConfig.gmsaAccount -RoleDefinitions @{ "$($artConfig.user)" = @{ 'RoleCapabilities' = 'RenameRunner' } } -path "$env:Temp\RenameRunner.pssc" New-PSRoleCapabilityFile -VisibleCmdlets @{ 'Name' = 'Rename-Computer'; 'Parameters' = @{ 'Name' = 'NewName'; 'ValidatePattern' = 'ATOMICSOC.*' }, @{ 'Name' = 'Force' }, @{ 'Name' = 'restart' } } -path "$path\RenameRunner.psrc" $null = Register-PSSessionConfiguration -name "RenameRunnerEndpoint" -path "$env:Temp\RenameRunner.pssc" -force Add-LocalGroupMember "administrators" "$($artConfig.gmsaAccount)$" -ErrorAction Ignore # Make sure WinRM is enabled and set to Automic start (not delayed) Set-ItemProperty hklm:\\SYSTEM\CurrentControlSet\Services\WinRM -Name Start -Value 2 Set-ItemProperty hklm:\\SYSTEM\CurrentControlSet\Services\WinRM -Name DelayedAutostart -Value 0 # default is delayed start and that is too slow given our 1 minute delay on our kickoff task # this registry key must be set to zero for things to work get-itemproperty hklm:\Software\Policies\Microsoft\Windows\WinRM\Service\ $hklmKey = (get-itemproperty hklm:\Software\Policies\Microsoft\Windows\WinRM\Service -name DisableRunAs -ErrorAction ignore).DisableRunAs $hkcuKey = (get-itemproperty hkcu:\Software\Policies\Microsoft\Windows\WinRM\Service -name DisableRunAs -ErrorAction ignore).DisableRunAs if ((1 -eq $hklmKey) -or (1 -eq $hkcuKey)) { Write-Host -ForegroundColor Red "DisableRunAs registry Key will not allow use of the JEA endpoint with a gmsa account" } if ((Get-ItemProperty hklm:\System\CurrentControlSet\Control\Lsa\ -name DisableDomainCreds).DisableDomainCreds) { Write-Host -ForegroundColor Red "Do not allow storage of passwords and credentials for network authentication must be disabled" } } if ($artConfig.OS -eq "windows") { if (Test-Path $artConfig.credFile) { Write-Host "Credential File $($artConfig.credFile) already exists, not prompting for creation of a new one." $cred = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $artConfig.user, (Get-Content $artConfig.credFile | ConvertTo-SecureString) } else { # create credential file for the user since we aren't using a group managed service account $cred = Get-Credential -UserName $artConfig.user -message "Enter password for $($artConfig.user) in order to create the runner scheduled task" $cred.Password | ConvertFrom-SecureString | Out-File $artConfig.credFile } # setup scheduled task that will start the runner after each restart # local security policy --> Local Policies --> Security Options --> Network access: Do not allow storage of passwords and credentials for network authentication must be disabled $taskName = "KickOff-AtomicRunner" Unregister-ScheduledTask $taskName -confirm:$false -ErrorAction Ignore # Windows scheduled task includes a 20 minutes sleep then restart if the call to Invoke-KickoffAtomicRunner fails # this occurs occassionally when Windows has issues logging into the runner user's account and logs in as a TEMP user $taskAction = New-ScheduledTaskAction -Execute "powershell.exe" -Argument "-exec bypass -Command Invoke-KickoffAtomicRunner; Start-Sleep 1200; Restart-Computer -Force" $taskPrincipal = New-ScheduledTaskPrincipal -UserId $artConfig.user $delays = @(1, 2, 4, 8, 16, 32, 64) # using multiple triggers as a retry mechanism because the built-in retry mechanism doesn't work when the computer renaming causes AD replication delays $triggers = @() foreach ($delay in $delays) { $trigger = New-ScheduledTaskTrigger -AtStartup $trigger.Delay = "PT$delay`M" $triggers += $trigger } $task = New-ScheduledTask -Action $taskAction -Principal $taskPrincipal -Trigger $triggers -Description "A task that runs 1 minute or later after boot to start the atomic test runner script" try { $null = Register-ScheduledTask -TaskName $taskName -InputObject $task -User $artConfig.user -Password $($cred.GetNetworkCredential().password) -ErrorAction Stop } catch { if ($_.CategoryInfo.Category -eq "AuthenticationError") { # remove the credential file if the password didn't work Write-Error "The credentials you entered are incorrect. Please run the setup script again and double check the username and password." Remove-Item $artConfig.credFile } else { Throw $_ } } } else { # sets cronjob string using basepath from config.ps1 $pwshPath = which pwsh $job = "@reboot root sleep 60;$pwshPath -Command Invoke-KickoffAtomicRunner" $exists = cat /etc/crontab | Select-String -Quiet "KickoffAtomicRunner" #checks if the Kickoff-AtomicRunner job exists. If not appends it to the system crontab. if ($null -eq $exists) { $(Write-Output "$job" >> /etc/crontab) write-host "setting cronjob" } else { write-host "cronjob already exists" } } # Add Import-Module statement to the PowerShell profile $root = Split-Path $PSScriptRoot -Parent $pathToPSD1 = Join-Path $root "Invoke-AtomicRedTeam.psd1" $importStatement = "Import-Module ""$pathToPSD1"" -Force" New-Item $PROFILE -ErrorAction Ignore $profileContent = Get-Content $profile $line = $profileContent | Select-String ".*import-module.*invoke-atomicredTeam.psd1" | Select-Object -ExpandProperty Line if ($line) { $profileContent | ForEach-Object { $_.replace( $line, "$importStatement") } | Set-Content $profile } else { Add-Content $profile $importStatement } # Install the Posh-SYLOG module if we are configured to use it and it is not already installed if ((-not (Get-Module -ListAvailable "Posh-SYSLOG")) -and [bool]$artConfig.syslogServer -and [bool]$artConfig.syslogPort) { write-verbose "Posh-SYSLOG" Install-Module -Name Posh-SYSLOG -Scope CurrentUser -Force } # create the CSV schedule of atomics to run if it doesn't exist if (-not (Test-Path $artConfig.scheduleFile)) { Invoke-GenerateNewSchedule } $schedule = Get-Schedule if ($null -eq $schedule) { Write-Host -ForegroundColor Yellow "There are no tests enabled on the schedule, set the 'Enabled' column to 'True' for the atomic test that you want to run. The schedule file is found here: $($artConfig.scheduleFile)" Write-Host -ForegroundColor Yellow "Rerun this setup script after updating the schedule" } else { # Get the prereqs for all of the tests on the schedule Invoke-AtomicRunner -GetPrereqs } } |