mirror of
https://github.com/Icinga/icinga-powershell-framework.git
synced 2025-12-20 23:00:35 -05:00
The former implementation had 5 threads permanently spinning fast (10ms sleep) while waiting for a REST connection to process. This causes higher load in general and it breaks systems where "Turn on PowerShell Script Block Logging policy" is enabled, because then each PS statement including Start-Sleep is logged - resulting in 500 event log entries per second. It's a suggested setting in some hardening guidelines. We can easily replace the Queue with a BlockingCollection backed by a ConcurrentQueue, which has the built-in feature to sleep until there are new items. Now the REST API threads consumes zero CPU time while waiting.
106 lines
5.4 KiB
PowerShell
106 lines
5.4 KiB
PowerShell
function New-IcingaForWindowsRESTThread()
|
|
{
|
|
param (
|
|
$RequireAuth,
|
|
$ThreadId
|
|
);
|
|
|
|
# Initialise our performance counter categories
|
|
Show-IcingaPerformanceCounterCategories | Out-Null;
|
|
|
|
while ($TRUE) {
|
|
|
|
try {
|
|
if ($Global:Icinga.Public.Daemons.RESTApi.ApiRequests.ContainsKey($ThreadId) -eq $FALSE) {
|
|
Start-Sleep -Milliseconds 10;
|
|
continue;
|
|
}
|
|
|
|
# block sleeping until content available
|
|
$Connection = $Global:Icinga.Public.Daemons.RESTApi.ApiRequests.$ThreadId.Take();
|
|
|
|
# Read the received message from the stream by using our smart functions
|
|
[string]$RestMessage = Read-IcingaTCPStream -Client $Connection.Client -Stream $Connection.Stream;
|
|
# Now properly translate the entire rest message to a parsable hashtable
|
|
$RESTRequest = Read-IcingaRESTMessage -RestMessage $RestMessage -Connection $Connection;
|
|
|
|
if ($null -ne $RESTRequest) {
|
|
|
|
# Check if we require to authenticate the user
|
|
if ($RequireAuth) {
|
|
# If no authentication header is provided we should show the prompt
|
|
if ([string]::IsNullOrEmpty($RESTRequest.Header.Authorization)) {
|
|
# In case we do not send an authentication header increase the blacklist counter
|
|
# to ensure we are not spammed and "attacked" by a client with useless requests
|
|
Add-IcingaRESTClientBlacklistCount `
|
|
-Client $Connection.Client `
|
|
-ClientList $Global:Icinga.Public.Daemons.RESTApi.ClientBlacklist;
|
|
# Send the authentication prompt
|
|
Send-IcingaWebAuthMessage -Connection $Connection;
|
|
# Close the connection
|
|
Close-IcingaTCPConnection -Client $Connection.Client;
|
|
continue;
|
|
}
|
|
|
|
$Credentials = Convert-Base64ToCredentials -AuthString $RESTRequest.Header.Authorization;
|
|
[bool]$LoginSuccess = Test-IcingaRESTCredentials -UserName $Credentials.user -Password $Credentials.password -Domain $Credentials.domain;
|
|
$Credentials = $null;
|
|
|
|
# Handle login failures
|
|
if ($LoginSuccess -eq $FALSE) {
|
|
# Failed attempts should increase the blacklist counter
|
|
Add-IcingaRESTClientBlacklistCount `
|
|
-Client $Connection.Client `
|
|
-ClientList $Global:Icinga.Public.Daemons.RESTApi.ClientBlacklist;
|
|
# Re-send the authentication prompt
|
|
Send-IcingaWebAuthMessage -Connection $Connection;
|
|
# Close the connection
|
|
Close-IcingaTCPConnection -Client $Connection.Client;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
# Set our thread being active
|
|
Set-IcingaForWindowsThreadAlive -ThreadName $Global:Icinga.Protected.ThreadName -Active -TerminateAction @{ 'Command' = 'Close-IcingaTCPConnection'; 'Arguments' = @{ 'Client' = $Connection.Client } };
|
|
|
|
# We should remove clients from the blacklist who are sending valid requests
|
|
Remove-IcingaRESTClientBlacklist -Client $Connection.Client -ClientList $Global:Icinga.Public.Daemons.RESTApi.ClientBlacklist;
|
|
switch (Get-IcingaRESTPathElement -Request $RESTRequest -Index 0) {
|
|
'v1' {
|
|
Invoke-IcingaRESTAPIv1Calls -Request $RESTRequest -Connection $Connection;
|
|
break;
|
|
};
|
|
default {
|
|
Write-IcingaDebugMessage -Message ('Invalid API call - no version specified' + ($RESTRequest.RequestPath | Out-String));
|
|
Send-IcingaTCPClientMessage -Message (
|
|
New-IcingaTCPClientRESTMessage `
|
|
-HTTPResponse ($IcingaHTTPEnums.HTTPResponseType.'Not Found') `
|
|
-ContentBody 'Invalid API call received. No version specified.'
|
|
) -Stream $Connection.Stream;
|
|
};
|
|
}
|
|
|
|
# set our thread no longer be active. We do this, because below there is no way we can
|
|
# actually get stuck on a endless loop, caused by external modules
|
|
Set-IcingaForWindowsThreadAlive -ThreadName $Global:Icinga.Protected.ThreadName;
|
|
}
|
|
} catch {
|
|
$ExMsg = $_.Exception.Message;
|
|
|
|
Send-IcingaTCPClientMessage -Message (
|
|
New-IcingaTCPClientRESTMessage `
|
|
-HTTPResponse ($IcingaHTTPEnums.HTTPResponseType.'Internal Server Error') `
|
|
-ContentBody $ExMsg
|
|
) -Stream $Connection.Stream;
|
|
|
|
Write-IcingaEventMessage -Namespace 'RESTApi' -EventId 2051 -ExceptionObject $_;
|
|
}
|
|
|
|
# Finally close the clients connection as we are done here and
|
|
# ensure this thread will close by simply leaving the function
|
|
Close-IcingaTCPConnection -Client $Connection.Client;
|
|
|
|
# Force Icinga for Windows Garbage Collection
|
|
Optimize-IcingaForWindowsMemory -ClearErrorStack;
|
|
}
|
|
}
|