Browse Source

test(lab): give each new fixture a new account for the effective-access case

When an account is deleted and created again with the same name, a Kerberos S4U
logon for it keeps returning the SID and the groups of the deleted account for
a while, on the domain controller, the client, and the file server. The matrix
deletes the fixture after each cell and creates it for the next, so the
effective-access tests of the Admin role found no access for the new account in
cells that followed within minutes (Windows Server 2022 cell, candidate and
baseline alike, shown by a probe that creates the accounts in a loop). A new
fixture now gets NtfsLiveSubject and four digits; a fixture that exists keeps
its account.

The end-state check of the matrix also reports leftover scheduled tasks, stage
folders, standard users, and probe accounts, which the review asked for.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
pull/119/head
Raimund Andree 2 days ago
parent
commit
1dec3893c1
  1. 3
      Tests/Lab/Acceptance/Run-MatrixSequence.ps1
  2. 17
      Tests/Lab/Acceptance/Test-MatrixCleanup.ps1
  3. 26
      Tests/Lab/Invoke-NTFSSecurityLabTest.ps1

3
Tests/Lab/Acceptance/Run-MatrixSequence.ps1

@ -89,7 +89,8 @@ foreach ($fileServerName in $cells) {
& (Join-Path -Path $kit -ChildPath 'Test-MatrixCleanup.ps1') -Mode Verify -SidFile $sidFile -OutFile (Join-Path -Path $cell -ChildPath 'cleanup-3-verify.log') @common & (Join-Path -Path $kit -ChildPath 'Test-MatrixCleanup.ps1') -Mode Verify -SidFile $sidFile -OutFile (Join-Path -Path $cell -ChildPath 'cleanup-3-verify.log') @common
$verify = Get-Content -LiteralPath (Join-Path -Path $cell -ChildPath 'cleanup-3-verify.log') -Raw $verify = Get-Content -LiteralPath (Join-Path -Path $cell -ChildPath 'cleanup-3-verify.log') -Raw
$clean = ($verify -match 'matrix-cleanup-Verify-DONE') -and ($verify -notmatch 'OU NTFSSecurityLive: True') -and ($verify -notmatch 'accounts: NtfsLive') -and $clean = ($verify -match 'matrix-cleanup-Verify-DONE') -and ($verify -notmatch 'OU NTFSSecurityLive: True') -and ($verify -notmatch 'accounts: NtfsLive') -and
($verify -notmatch 'share=True|C:\\NTFSSecurityLive=True|C:\\NTFSSecurityLab=True|NtfsLiveLocal=True') -and ($verify -notmatch 'profiles=[1-9]') -and ($verify -notmatch ': [1-9]\d* fixture member') ($verify -notmatch 'share=True|C:\\NTFSSecurityLive=True|C:\\NTFSSecurityLab=True|NtfsLiveLocal=True') -and ($verify -notmatch 'profiles=[1-9]') -and ($verify -notmatch ': [1-9]\d* fixture member') -and
($verify -notmatch 'probe accounts: [1-9]') -and ($verify -notmatch 'residue: [^\r\n]*=[1-9]')
Write-Sequence ("cell $fileServerName cleanup verdict from the verify log: {0}" -f $(if ($clean) { 'CLEAN' } else { 'DIRTY (read cleanup-3-verify.log)' })) Write-Sequence ("cell $fileServerName cleanup verdict from the verify log: {0}" -f $(if ($clean) { 'CLEAN' } else { 'DIRTY (read cleanup-3-verify.log)' }))
} }
} }

17
Tests/Lab/Acceptance/Test-MatrixCleanup.ps1

@ -11,7 +11,8 @@ param (
# Independent end-state check of the fixture of Invoke-NTFSSecurityLabTest.ps1 in a lab (Windows PowerShell 5.1, on the host). Snapshot # Independent end-state check of the fixture of Invoke-NTFSSecurityLabTest.ps1 in a lab (Windows PowerShell 5.1, on the host). Snapshot
# records the SIDs of the NtfsLive* accounts while the fixture exists. Verify reads the domains and every machine again, and reports # records the SIDs of the NtfsLive* accounts while the fixture exists. Verify reads the domains and every machine again, and reports
# the organizational unit, the accounts, the share, the folders, the local group, the memberships of Administrators, Access Control # the organizational unit, the accounts, the share, the folders, the local group, the memberships of Administrators, Access Control
# Assistance Operators, and Remote Management Users, and the profiles of those SIDs. The result is judged from this log, never from # Assistance Operators, and Remote Management Users, and the profiles of those SIDs, and what the suite runs and the probes of the kit leave
# behind (scheduled tasks, stage folders, standard users, probe accounts of the domain). The result is judged from this log, never from
# the wrapper of the controller or a global error count. Repair is for a run whose removal failed: with the SIDs of the snapshot, it # the wrapper of the controller or a global error count. Repair is for a run whose removal failed: with the SIDs of the snapshot, it
# removes what that run left on the machines (the memberships, also of orphaned SIDs, which net localgroup deletes by SID; the share; the # removes what that run left on the machines (the memberships, also of orphaned SIDs, which net localgroup deletes by SID; the share; the
# local group; the folders) and then reports like Verify. # local group; the folders) and then reports like Verify.
@ -28,16 +29,17 @@ param (
$domain = Get-ADDomain $domain = Get-ADDomain
$unit = Get-ADOrganizationalUnit -LDAPFilter '(ou=NTFSSecurityLive)' -SearchBase $domain.DistinguishedName -SearchScope OneLevel -Server $domain.PDCEmulator $unit = Get-ADOrganizationalUnit -LDAPFilter '(ou=NTFSSecurityLive)' -SearchBase $domain.DistinguishedName -SearchScope OneLevel -Server $domain.PDCEmulator
[pscustomobject]@{ [pscustomobject]@{
Domain = $domain.DNSRoot Domain = $domain.DNSRoot
Unit = [bool] $unit Unit = [bool] $unit
Sids = @(Get-ADObject -LDAPFilter '(sAMAccountName=NtfsLive*)' -SearchBase $domain.DistinguishedName -Server $domain.PDCEmulator -Properties objectSid, sAMAccountName | Sids = @(Get-ADObject -LDAPFilter '(sAMAccountName=NtfsLive*)' -SearchBase $domain.DistinguishedName -Server $domain.PDCEmulator -Properties objectSid, sAMAccountName |
ForEach-Object -Process { '{0}={1}' -f $_.sAMAccountName, $_.objectSid.Value }) ForEach-Object -Process { '{0}={1}' -f $_.sAMAccountName, $_.objectSid.Value })
ProbeAccounts = @(Get-ADObject -LDAPFilter '(sAMAccountName=NtfsProbe*)' -SearchBase $domain.DistinguishedName -Server $domain.PDCEmulator).Count
} }
} }
$directory = @(foreach ($name in $DomainController) { Invoke-LabCommand -ComputerName $name -ActivityName "Read the fixture of $name" -ScriptBlock $directoryScript @labCommand }) $directory = @(foreach ($name in $DomainController) { Invoke-LabCommand -ComputerName $name -ActivityName "Read the fixture of $name" -ScriptBlock $directoryScript @labCommand })
foreach ($state in $directory) { foreach ($state in $directory) {
'{0,-14} OU NTFSSecurityLive: {1,-5} NtfsLive* accounts: {2}' -f $state.Domain, $state.Unit, ($(if ($state.Sids) { $state.Sids -join ', ' } else { 'none' })) '{0,-14} OU NTFSSecurityLive: {1,-5} NtfsLive* accounts: {2}; probe accounts: {3}' -f $state.Domain, $state.Unit, ($(if ($state.Sids) { $state.Sids -join ', ' } else { 'none' })), $state.ProbeAccounts
} }
if ($Mode -eq 'Snapshot') { if ($Mode -eq 'Snapshot') {
@ -65,6 +67,10 @@ param (
LocalGroup = [bool] (Get-LocalGroup -Name 'NtfsLiveLocal' -ErrorAction SilentlyContinue) LocalGroup = [bool] (Get-LocalGroup -Name 'NtfsLiveLocal' -ErrorAction SilentlyContinue)
Groups = $groups -join '; ' Groups = $groups -join '; '
Profiles = @(Get-CimInstance -ClassName Win32_UserProfile | Where-Object -FilterScript { $_.SID -in $Sid }).Count Profiles = @(Get-CimInstance -ClassName Win32_UserProfile | Where-Object -FilterScript { $_.SID -in $Sid }).Count
# What the suite runs and the probes of the kit leave behind: scheduled tasks, stage folders, and standard users
Tasks = @(Get-ScheduledTask -ErrorAction SilentlyContinue | Where-Object -FilterScript { $_.TaskName -like 'NtfsMatrix*' }).Count
Stages = @('C:\NtfsMatrixLocal', 'C:\NtfsMatrixProbe' | Where-Object -FilterScript { Test-Path -LiteralPath $_ }).Count
Users = @(Get-LocalUser -ErrorAction SilentlyContinue | Where-Object -FilterScript { $_.Name -like 'NtfsProbe*' }).Count
} }
} }
@ -105,6 +111,7 @@ param (
$state = Invoke-LabCommand -ComputerName $name -ActivityName "Check $name" -ScriptBlock $machineScript -ArgumentList (, $sids) @labCommand $state = Invoke-LabCommand -ComputerName $name -ActivityName "Check $name" -ScriptBlock $machineScript -ArgumentList (, $sids) @labCommand
'{0,-9} share={1} C:\NTFSSecurityLive={2} C:\NTFSSecurityLab={3} NtfsLiveLocal={4} profiles={5}' -f $name, $state.Share, $state.ShareRoot, $state.Payload, $state.LocalGroup, $state.Profiles '{0,-9} share={1} C:\NTFSSecurityLive={2} C:\NTFSSecurityLab={3} NtfsLiveLocal={4} profiles={5}' -f $name, $state.Share, $state.ShareRoot, $state.Payload, $state.LocalGroup, $state.Profiles
' {0}' -f $state.Groups ' {0}' -f $state.Groups
' residue: scheduled tasks={0} stage folders={1} probe users={2}' -f $state.Tasks, $state.Stages, $state.Users
} }
} }

26
Tests/Lab/Invoke-NTFSSecurityLabTest.ps1

@ -128,15 +128,16 @@ $roleAccounts = [ordered]@{
ServerAdmin = 'NtfsLiveServerAdmin' ServerAdmin = 'NtfsLiveServerAdmin'
Admin = 'NtfsLiveAdmin' Admin = 'NtfsLiveAdmin'
} }
$subjectAccount = 'NtfsLiveSubject' $subjectBaseName = 'NtfsLiveSubject'
$orphanAccount = 'NtfsLiveOrphan' $orphanAccount = 'NtfsLiveOrphan'
$foreignAccount = 'NtfsLiveForeign' $foreignAccount = 'NtfsLiveForeign'
# The rights that the entries of the foreign accounts grant on the folder of case 9, by position # The rights that the entries of the foreign accounts grant on the folder of case 9, by position
$foreignRights = 'ReadAndExecute', 'Modify', 'Write' $foreignRights = 'ReadAndExecute', 'Modify', 'Write'
$localGroupName = 'NtfsLiveLocal' $localGroupName = 'NtfsLiveLocal'
# The members of NtfsLiveInner follow when the name of the account of case 3 is known
$groupMembers = @{ $groupMembers = @{
NtfsLiveDelegates = @('NtfsLiveDelegate') NtfsLiveDelegates = @('NtfsLiveDelegate')
NtfsLiveInner = @('NtfsLiveSubject') NtfsLiveInner = @()
NtfsLiveOuter = @('NtfsLiveInner') NtfsLiveOuter = @('NtfsLiveInner')
} }
# A name that no DNS server resolves (RFC 2606) # A name that no DNS server resolves (RFC 2606)
@ -300,6 +301,19 @@ function ConvertFrom-LabTestResult {
} }
#region Remote script blocks #region Remote script blocks
# Runs on the domain controller: returns the names of the accounts of case 3 that the organizational unit already has.
$findSubjectScript = {
param ($OrganizationalUnitName, $BaseName)
$ErrorActionPreference = 'Stop'
Import-Module -Name ActiveDirectory
$domain = Get-ADDomain
$path = 'OU={0},{1}' -f $OrganizationalUnitName, $domain.DistinguishedName
if (Get-ADOrganizationalUnit -LDAPFilter "(ou=$OrganizationalUnitName)" -SearchBase $domain.DistinguishedName -SearchScope OneLevel -Server $domain.PDCEmulator) {
Get-ADUser -LDAPFilter "(sAMAccountName=$BaseName*)" -SearchBase $path -Server $domain.PDCEmulator | ForEach-Object -Process { $_.SamAccountName }
}
}
# Runs on the domain controller: creates or updates the accounts and groups in their organizational unit, pushes them # Runs on the domain controller: creates or updates the accounts and groups in their organizational unit, pushes them
# to the other domain controllers of the domain, and returns their SIDs. # to the other domain controllers of the domain, and returns their SIDs.
$accountScript = { $accountScript = {
@ -1060,6 +1074,14 @@ $modules = @(
) )
Write-LabProgress 'Preparing the accounts, the file server, and the client' Write-LabProgress 'Preparing the accounts, the file server, and the client'
# When an account is deleted and created again with the same name, a Kerberos S4U logon for it keeps returning the SID and the groups of
# the deleted account for a while: on the domain controller, the client, and the file server of the operating-system matrix, for every
# version of the module. The Authz functions behind Get-NTFSEffectiveAccess log an account on this way, so the cmdlet returned no access
# for the new account. A new fixture therefore gets a name for the account of case 3 that no earlier fixture used; a fixture that
# exists keeps its account.
$existingSubjects = @(Invoke-LabCommand -ComputerName $DomainController -ActivityName 'Look for the account of case 3' -ScriptBlock $findSubjectScript -ArgumentList $organizationalUnitName, $subjectBaseName @labCommand)
$subjectAccount = if ($existingSubjects) { [string]$existingSubjects[0] } else { '{0}{1:D4}' -f $subjectBaseName, (Get-Random -Minimum 0 -Maximum 10000) }
$groupMembers['NtfsLiveInner'] = @($subjectAccount)
$passwords = @{} $passwords = @{}
foreach ($name in @($roleAccounts.Values) + $subjectAccount) { foreach ($name in @($roleAccounts.Values) + $subjectAccount) {
$passwords[$name] = New-LabPassword $passwords[$name] = New-LabPassword

Loading…
Cancel
Save