Merge pull request #843 from Icinga:fix/security_catalog_compilation_errors

Fix: Security catalog compilation on non-english Windows versions

Fixes security catalog compilation error on non-english Windows versions, while properly skipping checks on system SID's and improves security by always adding the `SeDenyNetworkLogonRight` and `SeDenyInteractiveLogonRight` privilege section for the JEA user SID
This commit is contained in:
Lord Hepipud 2025-12-23 17:26:44 +01:00 committed by GitHub
commit 25f1814473
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 115 additions and 53 deletions

View file

@ -14,6 +14,7 @@ Released closed milestones can be found on [GitHub](https://github.com/Icinga/ic
### Bugfixes
* [#833](https://github.com/Icinga/icinga-powershell-framework/issues/833) Fixes registry lookup for Icinga Agent installation to check if the required `DisplayName` attribute is defined before checking
* [#834](https://github.com/Icinga/icinga-powershell-framework/issues/834) Fixes security catalog compilation error on non-english Windows versions, while properly skipping checks on system SID's and improves security by always adding the `SeDenyNetworkLogonRight` and `SeDenyInteractiveLogonRight` privilege section for the JEA user SID
* [#835](https://github.com/Icinga/icinga-powershell-framework/pull/835) Fixes JEA compiler to always enforce a rebuild of the Framework to ensure integrity of JEA profiles
* [#836](https://github.com/Icinga/icinga-powershell-framework/issues/836) Fixes Metric over Time collector not working on Windows 2012 R2 and older

View file

@ -5,42 +5,15 @@ function Set-IcingaAgentServicePermission()
return;
}
$SystemPermissions = New-IcingaTemporaryFile;
$ServiceUser = Get-IcingaServiceUser;
$ServiceUserSID = Get-IcingaUserSID $ServiceUser;
$SystemContent = Get-IcingaAgentServicePermission;
$NewSystemContent = @();
$ServiceUser = Get-IcingaServiceUser;
$ServiceUserSID = Get-IcingaUserSID $ServiceUser;
if ([string]::IsNullOrEmpty($ServiceUser)) {
Write-IcingaTestOutput -Severity 'Failed' -Message 'There is no user assigned to the Icinga 2 service or the service is not yet installed';
return $FALSE;
}
foreach ($line in $SystemContent) {
if ($line -like '*SeServiceLogonRight*') {
$line = [string]::Format('{0},*{1}', $line, $ServiceUserSID);
}
$NewSystemContent += $line;
}
Write-IcingaFileSecure -File "$SystemPermissions.inf" -Value $NewSystemContent;
$SystemOutput = Start-IcingaProcess -Executable 'secedit.exe' -Arguments ([string]::Format('/import /cfg "{0}.inf" /db "{0}.sdb"', $SystemPermissions));
if ($SystemOutput.ExitCode -ne 0) {
throw ([string]::Format('Unable to import system permission information: {0}', $SystemOutput.Message));
return $null;
}
$SystemOutput = Start-IcingaProcess -Executable 'secedit.exe' -Arguments ([string]::Format('/configure /cfg "{0}.inf" /db "{0}.sdb"', $SystemPermissions));
if ($SystemOutput.ExitCode -ne 0) {
throw ([string]::Format('Unable to configure system permission information: {0}', $SystemOutput.Message));
return $null;
}
Remove-Item $SystemPermissions*;
Update-IcingaWindowsUserPermission -SID $ServiceUserSID;
Test-IcingaAgentServicePermission | Out-Null;
}

View file

@ -14,6 +14,12 @@ function Test-IcingaAgentServicePermission()
return $TRUE;
}
# Never update system SIDs
if ($ServiceUserSID.Length -le 16) {
Write-IcingaTestOutput -Severity 'Passed' -Message ([string]::Format('It seems the provided SID "{0}" is a system SID. Skipping permission check', $ServiceUserSID));
return $TRUE;
}
if ([string]::IsNullOrEmpty($ServiceUser)) {
if (-Not $Silent) {
Write-IcingaTestOutput -Severity 'Failed' -Message 'There is no user assigned to the Icinga 2 service or the service is not yet installed';

View file

@ -11,6 +11,10 @@ function Uninstall-IcingaServiceUser()
Write-IcingaConsoleNotice 'Uninstalling user "{0}"' -Objects $IcingaUser;
# Fetch the current service user and SID
$ServiceUser = Get-IcingaServiceUser;
$ServiceUserSID = Get-IcingaUserSID $ServiceUser;
Stop-IcingaService 'icinga2';
Stop-IcingaForWindows;
@ -20,12 +24,9 @@ function Uninstall-IcingaServiceUser()
Set-IcingaServiceUser -User 'NT Authority\NetworkService' -Service 'icingapowershell' | Out-Null;
Set-IcingaUserPermissions -IcingaUser $IcingaUser -Remove;
Update-IcingaWindowsUserPermission -SID $ServiceUserSID -Remove;
$UserConfig = Remove-IcingaWindowsUser -IcingaUser $IcingaUser;
if ($null -ne $UserConfig -And ([string]::IsNullOrEmpty($UserConfig.SID) -eq $FALSE)) {
Update-IcingaWindowsUserPermission -SID $UserConfig.SID -Remove;
}
Remove-IcingaWindowsUser -IcingaUser $IcingaUser | Out-Null;
Restart-IcingaService 'icinga2';
Restart-IcingaForWindows;

View file

@ -30,28 +30,109 @@ function Update-IcingaWindowsUserPermission()
}
$SecurityProfile = '';
# These SID's are required to be present, as they ship with Windows by default
# On non-english Windows installations these SID's might be missing and requires us
# to ensure they are present
[string[]]$RequiredLoginSIDs = @(
'*S-1-5-80-0', # NT SERVICE\ALL SERVICES
'*S-1-5-99-0' # RESTRICTED SERVICES\ALL RESTRICTED SERVICES
);
if ($Remove -eq $FALSE) {
$SecurityProfile = Get-Content "$UpdatedProfile.inf";
foreach ($line in $SecurityProfile) {
if ($line -like '*SeServiceLogonRight*') {
$line = [string]::Format('{0},*{1}', $line, $SID);
}
if ($line -like '*SeDenyNetworkLogonRight*') {
$line = [string]::Format('{0},*{1}', $line, $SID);
}
if ($line -like '*SeDenyInteractiveLogonRight*') {
$line = [string]::Format('{0},*{1}', $line, $SID);
}
# Read the current security profile file
$SecurityProfile = Get-Content "$UpdatedProfile.inf";
[bool]$IsPrivilegedSection = $FALSE;
[bool]$HasDenyNetworkLogon = $FALSE;
[bool]$HasDenyInteractiveLogon = $FALSE;
foreach ($line in $SecurityProfile) {
if ($line -match '^\s*\[Privilege Rights\]\s*$') {
$IsPrivilegedSection = $TRUE;
$NewSecurityProfile += $line;
continue;
}
} else {
$SecurityProfile = Get-Content "$UpdatedProfile.inf" -Raw;
$SecurityProfile = $SecurityProfile.Replace([string]::Format(',*{0}', $SID), '');
$SecurityProfile = $SecurityProfile.Replace([string]::Format('*{0},', $SID), '');
$NewSecurityProfile = $SecurityProfile;
# Check for next section, which is [Version]
if ($IsPrivilegedSection -and $line -match '^\s*\[.*\]\s*$') {
$IsPrivilegedSection = $FALSE;
# If we are adding permissions, ensure the deny logon rights are present
if ($HasDenyNetworkLogon -eq $FALSE) {
Write-IcingaConsoleWarning 'Adding missing "SeDenyNetworkLogonRight" privilege to security profile';
$NewSecurityProfile += [string]::Format('SeDenyNetworkLogonRight = *{0}', $SID);
}
if ($HasDenyInteractiveLogon -eq $FALSE) {
Write-IcingaConsoleWarning 'Adding missing "SeDenyInteractiveLogonRight" privilege to security profile';
$NewSecurityProfile += [string]::Format('SeDenyInteractiveLogonRight = *{0}', $SID);
}
}
if ($line -match '^\s*(SeServiceLogonRight|SeDenyNetworkLogonRight|SeDenyInteractiveLogonRight)\s*=\s*(.*)$') {
[string]$privilegeName = $matches[1];
[string]$rhsValue = $matches[2];
if ($privilegeName -eq 'SeDenyNetworkLogonRight') {
$HasDenyNetworkLogon = $TRUE;
}
if ($privilegeName -eq 'SeDenyInteractiveLogonRight') {
$HasDenyInteractiveLogon = $TRUE;
}
[string[]]$entryList = @();
[string[]]$nonSidEntries = @();
if ([string]::IsNullOrEmpty($rhsValue) -eq $FALSE) {
[string[]]$tokenArray = $rhsValue -split ',';
foreach ($token in $tokenArray) {
$token = $token.Trim();
if ([string]::IsNullOrEmpty($token) -eq $FALSE) {
# Detect any entries that are not SIDs (SIDs start with '*' and S-1-...)
if (-not ($token -match '^\*S-1-\d+(-\d+)*$')) {
$nonSidEntries += $token;
continue;
}
if ($Remove -and $token -like "*$SID") {
Write-IcingaConsoleNotice 'Removing SID "{0}" from privilege "{1}"' -Objects $SID, $privilegeName;
continue;
}
$entryList += $token;
}
}
}
if ($nonSidEntries.Count -gt 0) {
Write-IcingaConsoleWarning 'Found non-SID entries for "{0}": {1}' -Objects $privilegeName, ($nonSidEntries -join ',');
}
# Ensure required login SIDs are present for SeServiceLogonRight
if ($privilegeName -eq 'SeServiceLogonRight') {
foreach ($requiredSID in $RequiredLoginSIDs) {
if ($entryList -notcontains $requiredSID) {
Write-IcingaConsoleWarning 'Adding missing default SID "{0}" for privilege "{1}"' -Objects $requiredSID, $privilegeName;
$entryList += $requiredSID;
}
}
}
# Ensure the managed user SID is present if we are not removing it
if ($Remove -eq $FALSE) {
[string]$managedSidEntry = "*$SID";
if ($entryList -notcontains $managedSidEntry) {
$entryList += $managedSidEntry;
}
} else {
Write-IcingaConsoleNotice 'Removing SID "{0}" from privilege "{1}"' -Objects $SID, $privilegeName;
}
# If we add an pricilege and there are no entries left, we still have to add the line with an empty value
# Windows will handle the empty value correctly and remove the line itself
$line = [string]::Format('{0} = {1}', $privilegeName, ($entryList -join ','));
}
$NewSecurityProfile += $line;
}
Set-Content -Path "$UpdatedProfile.inf" -Value $NewSecurityProfile;