mirror of
https://github.com/Icinga/icinga-powershell-framework.git
synced 2025-12-21 07:10:15 -05:00
Merge pull request #57 from Icinga/feature/rest_daemon
Feature/rest daemon
This commit is contained in:
commit
74967deb64
43 changed files with 1299 additions and 70 deletions
21
doc/20-Eventlog.md
Normal file
21
doc/20-Eventlog.md
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
# Framework Eventlog Documentation
|
||||
|
||||
Below you will find a list of EventId's which are exported by this module. The short and detailed message are both written directly into the eventlog. This documentation shall simply provide a summary of available EventId's
|
||||
|
||||
## Event Id 1000
|
||||
|
||||
| Category | Short Message | Detailed Message |
|
||||
| --- | --- | --- |
|
||||
| Information | Generic debug message issued by the Framework or its components | The Framework or is components can issue generic debug message in case the debug log is enabled. Please ensure to disable it, if not used. You can do so with the command "Disable-IcingaFrameworkDebugMode" |
|
||||
|
||||
## Event Id 1500
|
||||
|
||||
| Category | Short Message | Detailed Message |
|
||||
| --- | --- | --- |
|
||||
| Error | Failed to securely establish a communiation between this server and the client | A client connection could not be established to this server. This issue is mostly caused by using Self-Signed/Icinga 2 Agent certificates for the server and the client not trusting the certificate. To resolve this issue, either use trusted certificates signed by your trusted CA or setup the client to accept untrusted certificates |
|
||||
|
||||
## Event Id 1501
|
||||
|
||||
| Category | Short Message | Detailed Message |
|
||||
| --- | --- | --- |
|
||||
| Error | Client connection was interrupted because of invalid SSL stream | A client connection was terminated by the Framework because no secure SSL handshake could be established. This issue in general is followed by EventId 1500. |
|
||||
|
|
@ -7,7 +7,7 @@
|
|||
Copyright = '(c) 2020 Icinga GmbH | MIT'
|
||||
Description = 'Icinga for Windows module which allows to entirely monitor the Windows Host system.'
|
||||
PowerShellVersion = '4.0'
|
||||
FunctionsToExport = @( 'Use-Icinga', 'Import-IcingaLib', 'Publish-IcingaModuleManifests', 'Get-IcingaPluginDir', 'Get-IcingaCustomPluginDir', 'Get-IcingaCacheDir', 'Get-IcingaPowerShellConfigDir', 'Get-IcingaFrameworkRootPath', 'Get-IcingaPowerShellModuleFile' )
|
||||
FunctionsToExport = @( 'Use-Icinga', 'Import-IcingaLib', 'Publish-IcingaModuleManifests', 'Publish-IcingaEventlogDocumentation', 'Get-IcingaPluginDir', 'Get-IcingaCustomPluginDir', 'Get-IcingaCacheDir', 'Get-IcingaPowerShellConfigDir', 'Get-IcingaFrameworkRootPath', 'Get-IcingaPowerShellModuleFile' )
|
||||
CmdletsToExport = @()
|
||||
VariablesToExport = '*'
|
||||
AliasesToExport = @()
|
||||
|
|
|
|||
|
|
@ -13,7 +13,8 @@ function Use-Icinga()
|
|||
{
|
||||
param(
|
||||
[switch]$LibOnly = $FALSE,
|
||||
[switch]$Daemon = $FALSE
|
||||
[switch]$Daemon = $FALSE,
|
||||
[switch]$DebugMode = $FALSE
|
||||
);
|
||||
|
||||
# Ensure we autoload the Icinga Plugin collection, provided by the external
|
||||
|
|
@ -29,6 +30,8 @@ function Use-Icinga()
|
|||
Import-IcingaLib '\' -Init;
|
||||
|
||||
if ($LibOnly -eq $FALSE) {
|
||||
Register-IcingaEventLog;
|
||||
|
||||
$global:IcingaThreads = [hashtable]::Synchronized(@{});
|
||||
$global:IcingaThreadContent = [hashtable]::Synchronized(@{});
|
||||
$global:IcingaThreadPool = [hashtable]::Synchronized(@{});
|
||||
|
|
@ -38,8 +41,36 @@ function Use-Icinga()
|
|||
'IcingaThreadContent' = $global:IcingaThreadContent;
|
||||
'IcingaThreadPool' = $global:IcingaThreadPool;
|
||||
'FrameworkRunningAsDaemon' = $Daemon;
|
||||
'DebugMode' = $DebugMode;
|
||||
}
|
||||
);
|
||||
} else {
|
||||
# This will fix the debug mode in case we are only using Libs
|
||||
# without any other variable content and daemon handling
|
||||
if ($null -eq $global:IcingaDaemonData) {
|
||||
$global:IcingaDaemonData = [hashtable]::Synchronized(@{});
|
||||
}
|
||||
if ($global:IcingaDaemonData.ContainsKey('DebugMode') -eq $FALSE) {
|
||||
$global:IcingaDaemonData.DebugMode = $DebugMode;
|
||||
}
|
||||
if ($global:IcingaDaemonData.ContainsKey('FrameworkRunningAsDaemon') -eq $FALSE) {
|
||||
$global:IcingaDaemonData.FrameworkRunningAsDaemon = $Daemon;
|
||||
}
|
||||
}
|
||||
|
||||
# Enable DebugMode in case it is enabled in our config
|
||||
if (Get-IcingaFrameworkDebugMode) {
|
||||
Enable-IcingaFrameworkDebugMode;
|
||||
$DebugMode = $TRUE;
|
||||
}
|
||||
|
||||
$EventLogMessages = Invoke-IcingaNamespaceCmdlets -Command 'Register-IcingaEventLogMessages*';
|
||||
foreach ($entry in $EventLogMessages.Values) {
|
||||
foreach ($event in $entry.Keys) {
|
||||
Add-IcingaHashtableItem -Hashtable $global:IcingaEventLogEnums `
|
||||
-Key $event `
|
||||
-Value $entry[$event] | Out-Null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -159,6 +190,44 @@ function Publish-IcingaModuleManifests()
|
|||
Set-Content -Path $PSDFile -Value $NewContent;
|
||||
}
|
||||
|
||||
function Publish-IcingaEventlogDocumentation()
|
||||
{
|
||||
param(
|
||||
[string]$Namespace,
|
||||
[string]$OutFile
|
||||
);
|
||||
|
||||
[string]$DocContent = [string]::Format(
|
||||
'# {0} Eventlog Documentation',
|
||||
$Namespace
|
||||
);
|
||||
$DocContent += New-IcingaNewLine;
|
||||
$DocContent += New-IcingaNewLine;
|
||||
$DocContent += "Below you will find a list of EventId's which are exported by this module. The short and detailed message are both written directly into the eventlog. This documentation shall simply provide a summary of available EventId's";
|
||||
|
||||
$SortedArray = $IcingaEventLogEnums[$Namespace].Keys.GetEnumerator() | Sort-Object;
|
||||
|
||||
foreach ($entry in $SortedArray) {
|
||||
$entry = $IcingaEventLogEnums[$Namespace][$entry];
|
||||
|
||||
$DocContent = [string]::Format(
|
||||
'{0}{2}{2}## Event Id {1}{2}{2}| Category | Short Message | Detailed Message |{2}| --- | --- | --- |{2}| {3} | {4} | {5} |',
|
||||
$DocContent,
|
||||
$entry.EventId,
|
||||
(New-IcingaNewLine),
|
||||
$entry.EntryType,
|
||||
$entry.Message,
|
||||
$entry.Details
|
||||
);
|
||||
}
|
||||
|
||||
if ([string]::IsNullOrEmpty($OutFile)) {
|
||||
Write-Host $DocContent;
|
||||
} else {
|
||||
Set-Content -Path $OutFile -Value $DocContent;
|
||||
}
|
||||
}
|
||||
|
||||
function Get-IcingaPluginDir()
|
||||
{
|
||||
return (Join-Path -Path $PSScriptRoot -ChildPath 'lib\plugins\');
|
||||
|
|
|
|||
18
lib/core/framework/Disable-IcingaFrameworkDebugMode.psm1
Normal file
18
lib/core/framework/Disable-IcingaFrameworkDebugMode.psm1
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
<#
|
||||
.SYNOPSIS
|
||||
Disables the debug mode of the Framework
|
||||
.DESCRIPTION
|
||||
Disables the debug mode of the Framework
|
||||
.FUNCTIONALITY
|
||||
Disables the Icinga for Windows Debug-Log
|
||||
.EXAMPLE
|
||||
PS>Disable-IcingaFrameworkDebugMode;
|
||||
.LINK
|
||||
https://github.com/Icinga/icinga-powershell-framework
|
||||
#>
|
||||
|
||||
function Disable-IcingaFrameworkDebugMode()
|
||||
{
|
||||
$global:IcingaDaemonData.DebugMode = $FALSE;
|
||||
Set-IcingaPowerShellConfig -Path 'Framework.DebugMode' -Value $FALSE;
|
||||
}
|
||||
20
lib/core/framework/Enable-IcingaFrameworkDebugMode.psm1
Normal file
20
lib/core/framework/Enable-IcingaFrameworkDebugMode.psm1
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
<#
|
||||
.SYNOPSIS
|
||||
Enables the debug mode of the Framework to print additional details into
|
||||
the Windows Event Log with Id 1000
|
||||
.DESCRIPTION
|
||||
Enables the debug mode of the Framework to print additional details into
|
||||
the Windows Event Log with Id 1000
|
||||
.FUNCTIONALITY
|
||||
Enables the Icinga for Windows Debug-Log
|
||||
.EXAMPLE
|
||||
PS>Enable-IcingaFrameworkDebugMode;
|
||||
.LINK
|
||||
https://github.com/Icinga/icinga-powershell-framework
|
||||
#>
|
||||
|
||||
function Enable-IcingaFrameworkDebugMode()
|
||||
{
|
||||
$global:IcingaDaemonData.DebugMode = $TRUE;
|
||||
Set-IcingaPowerShellConfig -Path 'Framework.DebugMode' -Value $TRUE;
|
||||
}
|
||||
40
lib/core/framework/Get-IcingaCheckSchedulerPerfData.psm1
Normal file
40
lib/core/framework/Get-IcingaCheckSchedulerPerfData.psm1
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
<#
|
||||
.SYNOPSIS
|
||||
Function to fetch the last executed plugin peformance data
|
||||
from an internal memory cache in case the Framework is running as daemon.
|
||||
.DESCRIPTION
|
||||
While running the Framework as daemon, checkresults for plugins are not
|
||||
printed into the console but written into an internal memory cache. Once
|
||||
a plugin was executed, use this function to fetch the plugin performance data
|
||||
.FUNCTIONALITY
|
||||
Returns the last performance data output for executed plugins while the
|
||||
Framework is running as daemon
|
||||
.OUTPUTS
|
||||
System.Object
|
||||
.LINK
|
||||
https://github.com/Icinga/icinga-powershell-framework
|
||||
#>
|
||||
|
||||
function Get-IcingaCheckSchedulerPerfData()
|
||||
{
|
||||
if ($null -eq $IcingaDaemonData) {
|
||||
return $null;
|
||||
}
|
||||
|
||||
if ($IcingaDaemonData.ContainsKey('IcingaThreadContent') -eq $FALSE) {
|
||||
return $null;
|
||||
}
|
||||
|
||||
if ($IcingaDaemonData.IcingaThreadContent.ContainsKey('Scheduler') -eq $FALSE) {
|
||||
return $null;
|
||||
}
|
||||
|
||||
if ($IcingaDaemonData.IcingaThreadContent.Scheduler.ContainsKey('PluginPerfData') -eq $FALSE) {
|
||||
return $null;
|
||||
}
|
||||
|
||||
$PerfData = $IcingaDaemonData.IcingaThreadContent.Scheduler.PluginPerfData;
|
||||
$IcingaDaemonData.IcingaThreadContent.Scheduler.PluginPerfData = @();
|
||||
|
||||
return $PerfData;
|
||||
}
|
||||
40
lib/core/framework/Get-IcingaCheckSchedulerPluginOutput.psm1
Normal file
40
lib/core/framework/Get-IcingaCheckSchedulerPluginOutput.psm1
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
<#
|
||||
.SYNOPSIS
|
||||
Function to fetch the last executed plugin output from an internal memory
|
||||
cache in case the Framework is running as daemon.
|
||||
.DESCRIPTION
|
||||
While running the Framework as daemon, checkresults for plugins are not
|
||||
printed into the console but written into an internal memory cache. Once
|
||||
a plugin was executed, use this function to fetch the plugin output
|
||||
.FUNCTIONALITY
|
||||
Returns the last checkresult output for executed plugins while the
|
||||
Framework is running as daemon
|
||||
.OUTPUTS
|
||||
System.Object
|
||||
.LINK
|
||||
https://github.com/Icinga/icinga-powershell-framework
|
||||
#>
|
||||
|
||||
function Get-IcingaCheckSchedulerPluginOutput()
|
||||
{
|
||||
if ($null -eq $IcingaDaemonData) {
|
||||
return $null;
|
||||
}
|
||||
|
||||
if ($IcingaDaemonData.ContainsKey('IcingaThreadContent') -eq $FALSE) {
|
||||
return $null;
|
||||
}
|
||||
|
||||
if ($IcingaDaemonData.IcingaThreadContent.ContainsKey('Scheduler') -eq $FALSE) {
|
||||
return $null;
|
||||
}
|
||||
|
||||
if ($IcingaDaemonData.IcingaThreadContent.Scheduler.ContainsKey('PluginCache') -eq $FALSE) {
|
||||
return $null;
|
||||
}
|
||||
|
||||
$CheckResult = [string]::Join("`r`n", $IcingaDaemonData.IcingaThreadContent.Scheduler.PluginCache);
|
||||
$IcingaDaemonData.IcingaThreadContent.Scheduler.PluginCache = @();
|
||||
|
||||
return $CheckResult;
|
||||
}
|
||||
25
lib/core/framework/Get-IcingaFrameworkDebugMode.psm1
Normal file
25
lib/core/framework/Get-IcingaFrameworkDebugMode.psm1
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
<#
|
||||
.SYNOPSIS
|
||||
Get the current debug mode configuration of the Framework
|
||||
.DESCRIPTION
|
||||
Get the current debug mode configuration of the Framework
|
||||
.FUNCTIONALITY
|
||||
Get the current debug mode configuration of the Framework
|
||||
.EXAMPLE
|
||||
PS>Get-IcingaFrameworkDebugMode;
|
||||
.LINK
|
||||
https://github.com/Icinga/icinga-powershell-framework
|
||||
.OUTPUTS
|
||||
System.Boolean
|
||||
#>
|
||||
|
||||
function Get-IcingaFrameworkDebugMode()
|
||||
{
|
||||
$DebugMode = Get-IcingaPowerShellConfig -Path 'Framework.DebugMode';
|
||||
|
||||
if ($null -eq $DebugMode) {
|
||||
return $FALSE;
|
||||
}
|
||||
|
||||
return $DebugMode;
|
||||
}
|
||||
|
|
@ -39,23 +39,44 @@ function Get-IcingaPowerShellModuleArchive()
|
|||
param(
|
||||
[string]$DownloadUrl = '',
|
||||
[string]$ModuleName = '',
|
||||
[string]$Repository = ''
|
||||
[string]$Repository = '',
|
||||
[string]$GitHubUser = 'Icinga',
|
||||
[bool]$Stable = $FALSE,
|
||||
[bool]$Snapshot = $FALSE,
|
||||
[bool]$DryRun = $FALSE
|
||||
);
|
||||
|
||||
$ProgressPreference = "SilentlyContinue";
|
||||
$Tag = 'master';
|
||||
[bool]$SkipRepo = $FALSE;
|
||||
|
||||
if ($Stable -Or $Snapshot) {
|
||||
$SkipRepo = $TRUE;
|
||||
}
|
||||
|
||||
# Fix TLS errors while connecting to GitHub with old PowerShell versions
|
||||
[Net.ServicePointManager]::SecurityProtocol = "tls12, tls11";
|
||||
|
||||
if ([string]::IsNullOrEmpty($DownloadUrl)) {
|
||||
if ((Get-IcingaAgentInstallerAnswerInput -Prompt ([string]::Format('Do you provide a custom repository for "{0}"?', $ModuleName)) -Default 'n').result -eq 1) {
|
||||
$branch = (Get-IcingaAgentInstallerAnswerInput -Prompt 'Which version to you want to install? (snapshot/stable)' -Default 'v' -DefaultInput 'stable').answer
|
||||
if ($branch.ToLower() -eq 'snapshot') {
|
||||
$DownloadUrl = [string]::Format('https://github.com/Icinga/{0}/archive/master.zip', $Repository);
|
||||
if ($SkipRepo -Or (Get-IcingaAgentInstallerAnswerInput -Prompt ([string]::Format('Do you provide a custom repository for "{0}"?', $ModuleName)) -Default 'n').result -eq 1) {
|
||||
if ($Stable -eq $FALSE -And $Snapshot -eq $FALSE) {
|
||||
$branch = (Get-IcingaAgentInstallerAnswerInput -Prompt 'Which version to you want to install? (snapshot/stable)' -Default 'v' -DefaultInput 'stable').answer;
|
||||
} elseif ($Stable) {
|
||||
$branch = 'stable';
|
||||
} else {
|
||||
$LatestRelease = (Invoke-WebRequest -Uri ([string]::Format('https://github.com/Icinga/{0}/releases/latest', $Repository)) -UseBasicParsing).BaseResponse.ResponseUri.AbsoluteUri;
|
||||
$branch = 'snapshot'
|
||||
}
|
||||
if ($branch.ToLower() -eq 'snapshot') {
|
||||
$DownloadUrl = [string]::Format('https://github.com/{0}/{1}/archive/master.zip', $GitHubUser, $Repository);
|
||||
} else {
|
||||
try {
|
||||
$LatestRelease = (Invoke-WebRequest -Uri ([string]::Format('https://github.com/{0}/{1}/releases/latest', $GitHubUser, $Repository)) -UseBasicParsing).BaseResponse.ResponseUri.AbsoluteUri;
|
||||
$DownloadUrl = $LatestRelease.Replace('/releases/tag/', '/archive/');
|
||||
$Tag = $DownloadUrl.Split('/')[-1];
|
||||
} catch {
|
||||
Write-Host 'Failed to fetch latest release from GitHub. Either the module or the GitHub account do not exist';
|
||||
}
|
||||
|
||||
$DownloadUrl = [string]::Format('{0}/{1}.zip', $DownloadUrl, $Tag);
|
||||
|
||||
$CurrentVersion = Get-IcingaPowerShellModuleVersion $Repository;
|
||||
|
|
@ -77,6 +98,17 @@ function Get-IcingaPowerShellModuleArchive()
|
|||
}
|
||||
}
|
||||
|
||||
if ($DryRun) {
|
||||
return @{
|
||||
'DownloadUrl' = $DownloadUrl;
|
||||
'Version' = $Tag;
|
||||
'Directory' = '';
|
||||
'Archive' = '';
|
||||
'ModuleRoot' = (Get-IcingaFrameworkRootPath);
|
||||
'Installed' = $FALSE;
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
$DownloadDirectory = New-IcingaTemporaryDirectory;
|
||||
$DownloadDestination = (Join-Path -Path $DownloadDirectory -ChildPath ([string]::Format('{0}.zip', $Repository)));
|
||||
|
|
|
|||
121
lib/core/framework/Install-IcingaFrameworkComponent.psm1
Normal file
121
lib/core/framework/Install-IcingaFrameworkComponent.psm1
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
<#
|
||||
.SYNOPSIS
|
||||
Installs a PowerShell Module within the 'icinga-powershell-' namespace
|
||||
from GitHub or custom locations and installs it into the module directory
|
||||
the Framework itself is installed to
|
||||
.DESCRIPTION
|
||||
Installs a PowerShell Module within the 'icinga-powershell-' namespace
|
||||
from GitHub or custom locations and installs it into the module directory
|
||||
the Framework itself is installed to
|
||||
.FUNCTIONALITY
|
||||
Download and install a PowerShell module from the 'icinga-powershell-' namespace
|
||||
.EXAMPLE
|
||||
PS>Install-IcingaFrameworkComponent -Name 'plugins' -Stable;
|
||||
.EXAMPLE
|
||||
PS>Install-IcingaFrameworkComponent -Name 'plugins' -Stable -DryRun;
|
||||
.PARAMETER Name
|
||||
The name of the module to install. The namespace 'icinga-powershell-' is added
|
||||
by the function automatically
|
||||
.PARAMETER GitHubUser
|
||||
Overwrite the default GitHub user for a different one to download modules from
|
||||
.PARAMETER Url
|
||||
Specify a direct Url to a ZIP-Archive for external or local web ressources or
|
||||
local network shares
|
||||
.PARAMETER Stable
|
||||
Download the latest stable version from a GitHub source
|
||||
.PARAMETER Snapshot
|
||||
Download the latest master branch from a GitHub source
|
||||
.PARAMETER DryRun
|
||||
Only fetch possible Urls and return the result. No download or installation
|
||||
will be done
|
||||
.INPUTS
|
||||
System.String
|
||||
.OUTPUTS
|
||||
System.Hashtable
|
||||
.LINK
|
||||
https://github.com/Icinga/icinga-powershell-framework
|
||||
#>
|
||||
|
||||
function Install-IcingaFrameworkComponent()
|
||||
{
|
||||
param(
|
||||
[string]$Name,
|
||||
[string]$GitHubUser = 'Icinga',
|
||||
[string]$Url,
|
||||
[switch]$Stable = $FALSE,
|
||||
[switch]$Snapshot = $FALSE,
|
||||
[switch]$DryRun = $FALSE
|
||||
);
|
||||
|
||||
if ([string]::IsNullOrEmpty($Name)) {
|
||||
throw 'Please specify a component name to install from a GitHub/Local space';
|
||||
}
|
||||
|
||||
$TextInfo = (Get-Culture).TextInfo;
|
||||
$ComponentName = $TextInfo.ToTitleCase($Name);
|
||||
$RepositoryName = [string]::Format('icinga-powershell-{0}', $Name);
|
||||
$Archive = Get-IcingaPowerShellModuleArchive `
|
||||
-DownloadUrl $Url `
|
||||
-GitHubUser $GitHubUser `
|
||||
-ModuleName (
|
||||
[string]::Format(
|
||||
'Icinga Component {0}', $ComponentName
|
||||
)
|
||||
) `
|
||||
-Repository $RepositoryName `
|
||||
-Stable $Stable `
|
||||
-Snapshot $Snapshot `
|
||||
-DryRun $DryRun;
|
||||
|
||||
if ($Archive.Installed -eq $FALSE -Or $DryRun) {
|
||||
return @{
|
||||
'RepoUrl' = $Archive.DownloadUrl
|
||||
};
|
||||
}
|
||||
|
||||
Write-Host ([string]::Format('Installing module into "{0}"', ($Archive.Directory)));
|
||||
Expand-IcingaZipArchive -Path $Archive.Archive -Destination $Archive.Directory | Out-Null;
|
||||
|
||||
$FolderContent = Get-ChildItem -Path $Archive.Directory;
|
||||
$ModuleContent = $Archive.Directory;
|
||||
|
||||
foreach ($entry in $FolderContent) {
|
||||
if ($entry -like ([string]::Format('{0}*', $RepositoryName))) {
|
||||
$ModuleContent = Join-Path -Path $ModuleContent -ChildPath $entry;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Write-Host ([string]::Format('Using content of folder "{0}" for updates', $ModuleContent));
|
||||
|
||||
$PluginDirectory = (Join-Path -Path $Archive.ModuleRoot -ChildPath $RepositoryName);
|
||||
|
||||
if ((Test-Path $PluginDirectory) -eq $FALSE) {
|
||||
Write-Host ([string]::Format('{0} Module Directory "{1}" is not present. Creating Directory', $ComponentName, $PluginDirectory));
|
||||
New-Item -Path $PluginDirectory -ItemType Directory | Out-Null;
|
||||
}
|
||||
|
||||
Write-Host ([string]::Format('Copying files to {0}', $ComponentName));
|
||||
Copy-ItemSecure -Path (Join-Path -Path $ModuleContent -ChildPath '/*') -Destination $PluginDirectory -Recurse -Force | Out-Null;
|
||||
|
||||
Write-Host 'Cleaning temporary content';
|
||||
Start-Sleep -Seconds 1;
|
||||
Remove-ItemSecure -Path $Archive.Directory -Recurse -Force | Out-Null;
|
||||
|
||||
Unblock-IcingaPowerShellFiles -Path $PluginDirectory;
|
||||
|
||||
# In case the plugins are not installed before, load the framework again to
|
||||
# include the plugins
|
||||
Use-Icinga;
|
||||
|
||||
# Unload the module if it was loaded before
|
||||
Remove-Module $RepositoryName -Force -ErrorAction SilentlyContinue;
|
||||
# Now import the module
|
||||
Import-Module $RepositoryName;
|
||||
|
||||
Write-Host ([string]::Format('Icinga component {0} update has been completed. Please start a new PowerShell to apply it', $ComponentName));
|
||||
|
||||
return @{
|
||||
'RepoUrl' = $Archive.DownloadUrl
|
||||
};
|
||||
}
|
||||
|
|
@ -30,52 +30,12 @@ function Install-IcingaFrameworkPlugins()
|
|||
[string]$PluginsUrl
|
||||
);
|
||||
|
||||
$RepositoryName = 'icinga-powershell-plugins';
|
||||
$Archive = Get-IcingaPowerShellModuleArchive -DownloadUrl $PluginsUrl -ModuleName 'Icinga Plugins' -Repository $RepositoryName;
|
||||
|
||||
if ($Archive.Installed -eq $FALSE) {
|
||||
return @{
|
||||
'PluginUrl' = $Archive.DownloadUrl
|
||||
};
|
||||
}
|
||||
|
||||
Write-IcingaConsoleNotice ([string]::Format('Installing module into "{0}"', ($Archive.Directory)));
|
||||
Expand-IcingaZipArchive -Path $Archive.Archive -Destination $Archive.Directory | Out-Null;
|
||||
|
||||
$FolderContent = Get-ChildItem -Path $Archive.Directory;
|
||||
$ModuleContent = $Archive.Directory;
|
||||
|
||||
foreach ($entry in $FolderContent) {
|
||||
if ($entry -like ([string]::Format('{0}*', $RepositoryName))) {
|
||||
$ModuleContent = Join-Path -Path $ModuleContent -ChildPath $entry;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Write-IcingaConsoleNotice ([string]::Format('Using content of folder "{0}" for updates', $ModuleContent));
|
||||
|
||||
$PluginDirectory = (Join-Path -Path $Archive.ModuleRoot -ChildPath $RepositoryName);
|
||||
|
||||
if ((Test-Path $PluginDirectory) -eq $FALSE) {
|
||||
Write-IcingaConsoleNotice ([string]::Format('Plugin Module Directory "{0}" is not present. Creating Directory', $PluginDirectory));
|
||||
New-Item -Path $PluginDirectory -ItemType Directory | Out-Null;
|
||||
}
|
||||
|
||||
Write-IcingaConsoleNotice 'Copying files to plugins';
|
||||
Copy-ItemSecure -Path (Join-Path -Path $ModuleContent -ChildPath '/*') -Destination $PluginDirectory -Recurse -Force | Out-Null;
|
||||
|
||||
Write-IcingaConsoleNotice 'Cleaning temporary content';
|
||||
Start-Sleep -Seconds 1;
|
||||
Remove-ItemSecure -Path $Archive.Directory -Recurse -Force | Out-Null;
|
||||
|
||||
Unblock-IcingaPowerShellFiles -Path $PluginDirectory;
|
||||
# In case the plugins are not installed before, load the framework again to
|
||||
# include the plugins
|
||||
Use-Icinga;
|
||||
|
||||
Write-IcingaConsoleNotice 'Icinga Plugin update has been completed';
|
||||
[Hashtable]$Result = Install-IcingaFrameworkComponent `
|
||||
-Name 'plugins' `
|
||||
-GitHubUser 'Icinga' `
|
||||
-Url $PluginsUrl;
|
||||
|
||||
return @{
|
||||
'PluginUrl' = $Archive.DownloadUrl
|
||||
'PluginUrl' = $Result.RepoUrl;
|
||||
};
|
||||
}
|
||||
|
|
|
|||
30
lib/core/framework/Invoke-IcingaNamespaceCmdlets.psm1
Normal file
30
lib/core/framework/Invoke-IcingaNamespaceCmdlets.psm1
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
function Invoke-IcingaNamespaceCmdlets()
|
||||
{
|
||||
param (
|
||||
[string]$Command
|
||||
);
|
||||
|
||||
[Hashtable]$CommandConfig = @{};
|
||||
|
||||
if ($Command.Contains('*') -eq $FALSE) {
|
||||
$Command = [string]::Format('{0}*', $Command);
|
||||
}
|
||||
|
||||
$CommandList = Get-Command $Command;
|
||||
|
||||
foreach ($Cmdlet in $CommandList) {
|
||||
try {
|
||||
$CmmandName = $Cmdlet.Name;
|
||||
Import-Module $Cmdlet.Module.Path -WarningAction SilentlyContinue -ErrorAction Stop;
|
||||
|
||||
$Content = (& $CmmandName);
|
||||
Add-IcingaHashtableItem -Hashtable $CommandConfig `
|
||||
-Key $Cmdlet.Name `
|
||||
-Value $Content | Out-Null;
|
||||
} catch {
|
||||
# TODO: Add event log logging on exceptions
|
||||
}
|
||||
}
|
||||
|
||||
return $CommandConfig;
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
function New-IcingaCheckSchedulerEnvironment()
|
||||
{
|
||||
$IcingaDaemonData.IcingaThreadContent.Add('Scheduler', @{ });
|
||||
if ($IcingaDaemonData.IcingaThreadContent['Scheduler'].ContainsKey('PluginCache') -eq $FALSE) {
|
||||
$IcingaDaemonData.IcingaThreadContent['Scheduler'].Add('PluginCache', @());
|
||||
$IcingaDaemonData.IcingaThreadContent['Scheduler'].Add('PluginPerfData', @());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
function Get-IcingaAgentHostCertificate()
|
||||
{
|
||||
# Default for Icinga 2.8.0 and above
|
||||
[string]$CertDirectory = (Join-Path -Path $Env:ProgramData -ChildPath 'icinga2\var\lib\icinga2\certs\*');
|
||||
$FolderContent = Get-ChildItem -Path $CertDirectory -Filter '*.crt' -Exclude 'ca.crt';
|
||||
$Hostname = Get-IcingaHostname -LowerCase $TRUE;
|
||||
$CertPath = $null;
|
||||
|
||||
foreach ($certFile in $FolderContent) {
|
||||
if ($certFile.Name.Contains($Hostname)) {
|
||||
$CertPath = $certFile.FullName;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ([string]::IsNullOrEmpty($CertPath)) {
|
||||
return $null;
|
||||
}
|
||||
|
||||
$Certificate = New-Object Security.Cryptography.X509Certificates.X509Certificate2 $CertPath;
|
||||
|
||||
return @{
|
||||
'CertFile' = $CertPath;
|
||||
'Subject' = $Certificate.Subject;
|
||||
'Thumbprint' = $Certificate.Thumbprint;
|
||||
};
|
||||
}
|
||||
31
lib/core/logging/Icinga_EventLog_Enums.psm1
Normal file
31
lib/core/logging/Icinga_EventLog_Enums.psm1
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
<#
|
||||
# This script will provide 'Enums' we can use for proper
|
||||
# error handling and to provide more detailed descriptions
|
||||
#
|
||||
# Example usage:
|
||||
# $IcingaEventLogEnums[2000]
|
||||
#>
|
||||
[hashtable]$IcingaEventLogEnums += @{
|
||||
'Framework' = @{
|
||||
1000 = @{
|
||||
'EntryType' = 'Information';
|
||||
'Message' = 'Generic debug message issued by the Framework or its components';
|
||||
'Details' = 'The Framework or is components can issue generic debug message in case the debug log is enabled. Please ensure to disable it, if not used. You can do so with the command "Disable-IcingaFrameworkDebugMode"';
|
||||
'EventId' = 1000;
|
||||
};
|
||||
1500 = @{
|
||||
'EntryType' = 'Error';
|
||||
'Message' = 'Failed to securely establish a communiation between this server and the client';
|
||||
'Details' = 'A client connection could not be established to this server. This issue is mostly caused by using Self-Signed/Icinga 2 Agent certificates for the server and the client not trusting the certificate. To resolve this issue, either use trusted certificates signed by your trusted CA or setup the client to accept untrusted certificates';
|
||||
'EventId' = 1500;
|
||||
};
|
||||
1501 = @{
|
||||
'EntryType' = 'Error';
|
||||
'Message' = 'Client connection was interrupted because of invalid SSL stream';
|
||||
'Details' = 'A client connection was terminated by the Framework because no secure SSL handshake could be established. This issue in general is followed by EventId 1500.';
|
||||
'EventId' = 1501;
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
Export-ModuleMember -Variable @( 'IcingaEventLogEnums' );
|
||||
12
lib/core/logging/Register-IcingaEventLog.psm1
Normal file
12
lib/core/logging/Register-IcingaEventLog.psm1
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
function Register-IcingaEventLog()
|
||||
{
|
||||
$Registered = [System.Diagnostics.EventLog]::SourceExists(
|
||||
'Icinga for Windows'
|
||||
);
|
||||
|
||||
if ($Registered) {
|
||||
return;
|
||||
}
|
||||
|
||||
New-EventLog -LogName Application -Source 'Icinga for Windows';
|
||||
}
|
||||
20
lib/core/logging/Write-IcingaDebugMessage.psm1
Normal file
20
lib/core/logging/Write-IcingaDebugMessage.psm1
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
function Write-IcingaDebugMessage()
|
||||
{
|
||||
param(
|
||||
[string]$Message,
|
||||
[array]$Objects = @()
|
||||
);
|
||||
|
||||
if ([string]::IsNullOrEmpty($Message)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($null -eq $global:IcingaDaemonData -Or $global:IcingaDaemonData.DebugMode -eq $FALSE) {
|
||||
return;
|
||||
}
|
||||
|
||||
[array]$DebugContent = @($Message);
|
||||
$DebugContent += $Objects;
|
||||
|
||||
Write-IcingaEventMessage -EventId 1000 -Namespace 'Framework' -Objects $DebugContent;
|
||||
}
|
||||
13
lib/core/logging/Write-IcingaErrorMessage.psm1
Normal file
13
lib/core/logging/Write-IcingaErrorMessage.psm1
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
function Write-IcingaErrorMessage()
|
||||
{
|
||||
param(
|
||||
[int]$EventId = 0,
|
||||
[string]$Message = $null
|
||||
);
|
||||
|
||||
if ($EventId -eq 0 -Or [string]::IsNullOrEmpty($Message)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Write-EventLog -LogName Application -Source 'Icinga for Windows' -EntryType Error -EventId $EventId -Message $Message;
|
||||
}
|
||||
59
lib/core/logging/Write-IcingaEventMessage.psm1
Normal file
59
lib/core/logging/Write-IcingaEventMessage.psm1
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
function Write-IcingaEventMessage()
|
||||
{
|
||||
param(
|
||||
[int]$EventId = 0,
|
||||
[string]$Namespace = $null,
|
||||
[array]$Objects = @()
|
||||
);
|
||||
|
||||
if ($EventId -eq 0 -Or [string]::IsNullOrEmpty($Namespace)) {
|
||||
return;
|
||||
}
|
||||
|
||||
[string]$EntryType = $IcingaEventLogEnums[$Namespace][$EventId].EntryType;
|
||||
[string]$Message = $IcingaEventLogEnums[$Namespace][$EventId].Message;
|
||||
[string]$Details = $IcingaEventLogEnums[$Namespace][$EventId].Details;
|
||||
|
||||
if ([string]::IsNullOrEmpty($Details)) {
|
||||
$Details = '';
|
||||
}
|
||||
if ([string]::IsNullOrEmpty($Message)) {
|
||||
$Message = '';
|
||||
}
|
||||
|
||||
[string]$ObjectDump = '';
|
||||
|
||||
if ($Objects.Count -eq 0) {
|
||||
$ObjectDump = [string]::Format(
|
||||
'{0}{0}No additional object details provided.',
|
||||
(New-IcingaNewLine)
|
||||
);
|
||||
}
|
||||
|
||||
foreach ($entry in $Objects) {
|
||||
$ObjectDump = [string]::Format(
|
||||
'{0}{1}',
|
||||
$ObjectDump,
|
||||
($entry | Out-String)
|
||||
);
|
||||
}
|
||||
|
||||
[string]$EventLogMessage = [string]::Format(
|
||||
'{0}{1}{1}{2}{1}{1}Object dumps if available:{1}{3}',
|
||||
$Message,
|
||||
(New-IcingaNewLine),
|
||||
$Details,
|
||||
$ObjectDump
|
||||
|
||||
);
|
||||
|
||||
if ($null -eq $EntryType -Or $null -eq $Message) {
|
||||
return;
|
||||
}
|
||||
|
||||
Write-EventLog -LogName Application `
|
||||
-Source 'Icinga for Windows' `
|
||||
-EntryType $EntryType `
|
||||
-EventId $EventId `
|
||||
-Message $EventLogMessage;
|
||||
}
|
||||
|
|
@ -12,6 +12,15 @@ function New-IcingaThreadInstance()
|
|||
$Name = New-IcingaThreadHash -ShellScript $ScriptBlock -Arguments $Arguments;
|
||||
}
|
||||
|
||||
Write-IcingaDebugMessage -Message (
|
||||
[string]::Format(
|
||||
'Creating new thread instance {0}{1}Arguments:{1}{2}',
|
||||
$Name,
|
||||
"`r`n",
|
||||
($Arguments | Out-String)
|
||||
)
|
||||
);
|
||||
|
||||
$Shell = [PowerShell]::Create();
|
||||
$Shell.RunspacePool = $ThreadPool;
|
||||
[void]$Shell.AddScript($ScriptBlock);
|
||||
|
|
|
|||
|
|
@ -1,14 +0,0 @@
|
|||
function Close-IcingaTcpConnection()
|
||||
{
|
||||
param(
|
||||
$TcpClient
|
||||
);
|
||||
|
||||
if ($null -eq $TcpClient) {
|
||||
return;
|
||||
}
|
||||
|
||||
$TcpClient.Close();
|
||||
$TcpClient.Dispose();
|
||||
$TcpClient = $null;
|
||||
}
|
||||
18
lib/core/tools/Get-IcingaHashtableItem.psm1
Normal file
18
lib/core/tools/Get-IcingaHashtableItem.psm1
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
function Get-IcingaHashtableItem()
|
||||
{
|
||||
param(
|
||||
$Hashtable,
|
||||
$Key,
|
||||
$NullValue = $null
|
||||
);
|
||||
|
||||
if ($null -eq $Hashtable) {
|
||||
return $NullValue;
|
||||
}
|
||||
|
||||
if ($Hashtable.ContainsKey($Key) -eq $FALSE) {
|
||||
return $NullValue;
|
||||
}
|
||||
|
||||
return $Hashtable[$Key];
|
||||
}
|
||||
48
lib/core/tools/Get-IcingaPSObjectProperties.psm1
Normal file
48
lib/core/tools/Get-IcingaPSObjectProperties.psm1
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
function Get-IcingaPSObjectProperties()
|
||||
{
|
||||
param(
|
||||
$Object = $null,
|
||||
[array]$Include = @(),
|
||||
[array]$Exclude = @()
|
||||
);
|
||||
|
||||
[hashtable]$RetValue = @{};
|
||||
|
||||
if ($null -eq $Object) {
|
||||
return $RetValue;
|
||||
}
|
||||
|
||||
foreach ($property in $Object.PSObject.Properties) {
|
||||
[string]$DataType = $property.TypeNameOfValue;
|
||||
|
||||
if ($Include.Count -ne 0 -And -Not ($Include -Contains $property.Name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($Exclude.Count -ne 0 -And $Exclude -Contains $property.Name) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($DataType.Contains('string') -or $DataType.Contains('int') -Or $DataType.Contains('bool')) {
|
||||
$RetValue.Add(
|
||||
$property.Name,
|
||||
$property.Value
|
||||
);
|
||||
} else {
|
||||
try {
|
||||
$RetValue.Add(
|
||||
$property.Name,
|
||||
(Get-IcingaPSObjectProperties -Object $property.Value)
|
||||
);
|
||||
} catch {
|
||||
$RetValue.Add(
|
||||
$property.Name,
|
||||
([string]$property.Value)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return $RetValue;
|
||||
}
|
||||
53
lib/core/tools/New-IcingaBasicAuthHeader.psm1
Normal file
53
lib/core/tools/New-IcingaBasicAuthHeader.psm1
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
<#
|
||||
.SYNOPSIS
|
||||
Creates a basic auth header for web requests in case the Get-Credential
|
||||
method is not supported or working properly
|
||||
.DESCRIPTION
|
||||
Creates a basic auth header for web requests in case the Get-Credential
|
||||
method is not supported or working properly
|
||||
.FUNCTIONALITY
|
||||
Creates a hashtable with a basic authorization header as Base64 encoded
|
||||
.EXAMPLE
|
||||
PS>New-IcingaBasicAuthHeader -Username 'example_user' -Password $SecurePasswordString;
|
||||
.EXAMPLE
|
||||
PS>New-IcingaBasicAuthHeader -Username 'example_user' -Password (Read-Host -Prompt 'Please enter your password' -AsSecureString);
|
||||
.EXAMPLE
|
||||
PS>New-IcingaBasicAuthHeader -Username 'example_user' -Password (ConvertTo-IcingaSecureString 'my_secret_password');
|
||||
.PARAMETER Username
|
||||
The user we will use to authenticate for
|
||||
.PARAMETER Password
|
||||
The password for the user provided as SecureString
|
||||
.INPUTS
|
||||
System.String
|
||||
.OUTPUTS
|
||||
System.Hashtable
|
||||
.LINK
|
||||
https://github.com/Icinga/icinga-powershell-framework
|
||||
#>
|
||||
|
||||
function New-IcingaBasicAuthHeader()
|
||||
{
|
||||
param(
|
||||
[string]$Username = $null,
|
||||
[SecureString]$Password = $null
|
||||
);
|
||||
|
||||
if ($null -eq $Password -or [string]::IsNullOrEmpty($Username)) {
|
||||
Write-Host 'Please specify your Username and Password to continue';
|
||||
return @{};
|
||||
}
|
||||
|
||||
$Credentials = [System.Convert]::ToBase64String(
|
||||
[System.Text.Encoding]::ASCII.GetBytes(
|
||||
[string]::Format(
|
||||
'{0}:{1}',
|
||||
$Username,
|
||||
(ConvertFrom-IcingaSecureString $Password)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
return @{
|
||||
'Authorization' = [string]::Format('Basic {0}', $Credentials)
|
||||
};
|
||||
}
|
||||
4
lib/core/tools/New-IcingaNewLine.psm1
Normal file
4
lib/core/tools/New-IcingaNewLine.psm1
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
function New-IcingaNewLine()
|
||||
{
|
||||
return "`r`n";
|
||||
}
|
||||
21
lib/web/Close-IcingaTCPConnection.psm1
Normal file
21
lib/web/Close-IcingaTCPConnection.psm1
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
function Close-IcingaTCPConnection()
|
||||
{
|
||||
param(
|
||||
[System.Net.Sockets.TcpClient]$Client = $null
|
||||
);
|
||||
|
||||
if ($null -eq $Client) {
|
||||
return;
|
||||
}
|
||||
|
||||
Write-IcingaDebugMessage -Message (
|
||||
[string]::Format(
|
||||
'Closing client connection for endpoint {0}',
|
||||
(Get-IcingaTCPClientRemoteEndpoint -Client $Client)
|
||||
)
|
||||
);
|
||||
|
||||
$Client.Close();
|
||||
$Client.Dispose();
|
||||
$Client = $null;
|
||||
}
|
||||
19
lib/web/Close-IcingaTCPSocket.psm1
Normal file
19
lib/web/Close-IcingaTCPSocket.psm1
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
function Close-IcingaTCPSocket()
|
||||
{
|
||||
param(
|
||||
[System.Net.Sockets.TcpListener]$Socket = $null
|
||||
);
|
||||
|
||||
if ($null -eq $Socket) {
|
||||
return;
|
||||
}
|
||||
|
||||
Write-IcingaDebugMessage -Message (
|
||||
[string]::Format(
|
||||
'Closing TCP socket {0}',
|
||||
$Socket.LocalEndpoint
|
||||
)
|
||||
);
|
||||
|
||||
$Socket.Stop();
|
||||
}
|
||||
66
lib/web/ConvertTo-IcingaX509Certificate.psm1
Normal file
66
lib/web/ConvertTo-IcingaX509Certificate.psm1
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
function ConvertTo-IcingaX509Certificate()
|
||||
{
|
||||
param(
|
||||
[string]$CertFile = $null,
|
||||
[string]$OutFile = $null,
|
||||
[switch]$Force = $FALSE
|
||||
);
|
||||
|
||||
if ([string]::IsNullOrEmpty($CertFile)) {
|
||||
throw 'Please specify a valid path to an existing certificate (.cer, .pem, .cert)';
|
||||
}
|
||||
|
||||
if ((Test-Path $CertFile) -eq $FALSE) {
|
||||
throw 'The provided path to your certificate was not valid';
|
||||
}
|
||||
|
||||
# Use an empty password for converted certificates
|
||||
$Password = $null;
|
||||
# Use a target file to specify if we use temp files or not
|
||||
$TargetFile = $OutFile;
|
||||
# Temp Cert
|
||||
[bool]$TempFile = $FALSE;
|
||||
|
||||
# Create a temp file to store the certificate in
|
||||
if ([string]::IsNullOrEmpty($OutFile)) {
|
||||
# Create a temporary file for full path and name
|
||||
$TargetFile = New-IcingaTemporaryFile;
|
||||
# Get the actual path to work with
|
||||
$TargetFile = $TargetFile.FullName;
|
||||
# Set internally that we are using a temp file
|
||||
$TempFile = $TRUE;
|
||||
# Delete the file again
|
||||
Remove-Item $TargetFile -Force -ErrorAction SilentlyContinue;
|
||||
}
|
||||
|
||||
# Convert our certificate if our target file does not exist
|
||||
# it is a temp file or we force its creation
|
||||
if (-Not (Test-Path $TargetFile) -Or $TempFile -Or $Force) {
|
||||
Write-Output "$Password
|
||||
$Password" | certutil -mergepfx "$CertFile" "$TargetFile" | Set-Variable -Name 'CertUtilOutput';
|
||||
}
|
||||
|
||||
Write-IcingaDebugMessage -Message (
|
||||
[string]::Format(
|
||||
'Certutil merge request has been completed. Certutil message:{0}{0}{1}',
|
||||
(New-IcingaNewLine),
|
||||
$CertUtilOutput
|
||||
)
|
||||
);
|
||||
|
||||
# If no target file exists afterwards (a valid PFX certificate)
|
||||
# then throw an exception
|
||||
if (-Not (Test-Path $TargetFile)) {
|
||||
throw 'The specified/created certificate file could not be found.';
|
||||
}
|
||||
|
||||
# Now load the actual certificate from the path
|
||||
$Certificate = New-Object Security.Cryptography.X509Certificates.X509Certificate2 $TargetFile;
|
||||
# Delete the PFX-Certificate which will be present after certutil merge
|
||||
if ($TempFile) {
|
||||
Remove-Item $TargetFile -Force -ErrorAction SilentlyContinue;
|
||||
}
|
||||
|
||||
# Return the certificate
|
||||
return $Certificate
|
||||
}
|
||||
14
lib/web/Disable-IcingaUntrustedCertificateValidation.psm1
Normal file
14
lib/web/Disable-IcingaUntrustedCertificateValidation.psm1
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
function Disable-IcingaUntrustedCertificateValidation()
|
||||
{
|
||||
try {
|
||||
[System.Net.ServicePointManager]::CertificatePolicy = $null;
|
||||
|
||||
Write-Host 'Successfully disabled untrusted certificate validation for this shell instance';
|
||||
} catch {
|
||||
Write-Host (
|
||||
[string]::Format(
|
||||
'Failed to disable untrusted certificate policy: {0}', $_.Exception.Message
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
27
lib/web/Enable-IcingaUntrustedCertificateValidation.psm1
Normal file
27
lib/web/Enable-IcingaUntrustedCertificateValidation.psm1
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
function Enable-IcingaUntrustedCertificateValidation()
|
||||
{
|
||||
try {
|
||||
# There is no other way as to use C# for this specific
|
||||
# case to configure the certificate validation check
|
||||
add-type @"
|
||||
using System.Net;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
|
||||
public class IcingaUntrustedCertificateValidation : ICertificatePolicy {
|
||||
public bool CheckValidationResult(ServicePoint srvPoint, X509Certificate certificate, WebRequest request, int certificateProblem) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
"@
|
||||
|
||||
[System.Net.ServicePointManager]::CertificatePolicy = New-Object IcingaUntrustedCertificateValidation;
|
||||
|
||||
Write-Host 'Successfully enabled untrusted certificate validation for this shell instance';
|
||||
} catch {
|
||||
Write-Host (
|
||||
[string]::Format(
|
||||
'Failed to enable untrusted certificate policy: {0}', $_.Exception.Message
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
17
lib/web/Get-IcingaRESTHeaderValue.psm1
Normal file
17
lib/web/Get-IcingaRESTHeaderValue.psm1
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
function Get-IcingaRESTHeaderValue()
|
||||
{
|
||||
param(
|
||||
[hashtable]$Request = @{},
|
||||
[string]$Header = $null
|
||||
);
|
||||
|
||||
if ($null -eq $Request -or [string]::IsNullOrEmpty($Header) -Or $Request.Count -eq 0) {
|
||||
return $null;
|
||||
}
|
||||
|
||||
if ($Request.Header.ContainsKey($Header) -eq $FALSE) {
|
||||
return $null
|
||||
}
|
||||
|
||||
return $Request.Header[$Header];
|
||||
}
|
||||
21
lib/web/Get-IcingaRESTPathElement.psm1
Normal file
21
lib/web/Get-IcingaRESTPathElement.psm1
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
function Get-IcingaRESTPathElement()
|
||||
{
|
||||
param(
|
||||
[Hashtable]$Request = @{},
|
||||
[int]$Index = 0
|
||||
);
|
||||
|
||||
if ($null -eq $Request -Or $Request.Count -eq 0) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if ($Request.ContainsKey('RequestPath') -eq $FALSE) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (($Index + 1) -gt $Request.RequestPath.PathArray.Count) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return $Request.RequestPath.PathArray[$Index];
|
||||
}
|
||||
47
lib/web/Get-IcingaSSLCertForSocket.psm1
Normal file
47
lib/web/Get-IcingaSSLCertForSocket.psm1
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
function Get-IcingaSSLCertForSocket()
|
||||
{
|
||||
param(
|
||||
[string]$CertFile = $null,
|
||||
[string]$CertThumbprint = $null
|
||||
);
|
||||
|
||||
# At first check if we assigned a cert file to use directly and check
|
||||
# if it is there and either import a PFX or use our convert function
|
||||
# to get a proper certificate
|
||||
if ([string]::IsNullOrEmpty($CertFile) -eq $FALSE) {
|
||||
if ((Test-Path $CertFile)) {
|
||||
$FileType = Get-Item -Path $CertFile;
|
||||
if ($FileType -eq '.pfx') {
|
||||
return (New-Object Security.Cryptography.X509Certificates.X509Certificate2 $CertFile);
|
||||
} else {
|
||||
return ConvertTo-IcingaX509Certificate -CertFile $CertFile;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# We could also have assigned a Thumbprint to use from the
|
||||
# Windows cert store. Try to look it up an return it if
|
||||
# it is found
|
||||
if ([string]::IsNullOrEmpty($CertThumbprint) -eq $FALSE) {
|
||||
$Certificates = Get-ChildItem -Path 'cert:\*' -Recurse `
|
||||
-Include $CertThumbprint `
|
||||
-ErrorAction SilentlyContinue `
|
||||
-WarningAction SilentlyContinue;
|
||||
|
||||
if ($Certificates.Count -ne 0) {
|
||||
return $Certificates[0];
|
||||
}
|
||||
}
|
||||
|
||||
# If no cert file or thumbprint was specified or simpy as fallback,
|
||||
# we should use the Icinga 2 Agent certificates
|
||||
$AgentCertificate = Get-IcingaAgentHostCertificate;
|
||||
|
||||
# If Agent is not installed or certificates were not found,
|
||||
# simply return null
|
||||
if ($null -eq $AgentCertificate) {
|
||||
return $null;
|
||||
}
|
||||
|
||||
return (ConvertTo-IcingaX509Certificate -CertFile $AgentCertificate.CertFile);
|
||||
}
|
||||
12
lib/web/Get-IcingaTCPClientRemoteEndpoint.psm1
Normal file
12
lib/web/Get-IcingaTCPClientRemoteEndpoint.psm1
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
function Get-IcingaTCPClientRemoteEndpoint()
|
||||
{
|
||||
param(
|
||||
[System.Net.Sockets.TcpClient]$Client = $null
|
||||
);
|
||||
|
||||
if ($null -eq $Client) {
|
||||
return 'unknown';
|
||||
}
|
||||
|
||||
return $Client.Client.RemoteEndPoint;
|
||||
}
|
||||
36
lib/web/Icinga_HTTPResponse_Enums.psm1
Normal file
36
lib/web/Icinga_HTTPResponse_Enums.psm1
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
<#
|
||||
# This script will provide 'Enums' we can use within our module to
|
||||
# easier access constants and to maintain a better overview of the
|
||||
# entire components
|
||||
#>
|
||||
[hashtable]$HTTPResponseCode = @{
|
||||
200 = 'Ok';
|
||||
400 = 'Bad Request';
|
||||
401 = 'Unauthorized';
|
||||
403 = 'Forbidden';
|
||||
404 = 'Not Found'
|
||||
500 = 'Internal Server Error';
|
||||
};
|
||||
|
||||
[hashtable]$HTTPResponseType = @{
|
||||
'Ok' = 200;
|
||||
'Bad Request' = 400;
|
||||
'Unauthorized' = 401;
|
||||
'Forbidden' = 403;
|
||||
'Not Found' = 404;
|
||||
'Internal Server Error' = 500;
|
||||
};
|
||||
|
||||
<#
|
||||
# Once we defined a new enum hashtable above, simply add it to this list
|
||||
# to make it available within the entire module.
|
||||
#
|
||||
# Example usage:
|
||||
# $IcingaHTTPEnums.HTTPResponseType.Ok
|
||||
#>
|
||||
[hashtable]$IcingaHTTPEnums = @{
|
||||
HTTPResponseCode = $HTTPResponseCode;
|
||||
HTTPResponseType = $HTTPResponseType;
|
||||
}
|
||||
|
||||
Export-ModuleMember -Variable @( 'IcingaHTTPEnums' );
|
||||
21
lib/web/New-IcingaSSLStream.psm1
Normal file
21
lib/web/New-IcingaSSLStream.psm1
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
function New-IcingaSSLStream()
|
||||
{
|
||||
param(
|
||||
[System.Net.Sockets.TcpClient]$Client = $null,
|
||||
[Security.Cryptography.X509Certificates.X509Certificate2]$Certificate = $null
|
||||
);
|
||||
|
||||
if ($null -eq $Client) {
|
||||
return $null;
|
||||
}
|
||||
|
||||
try {
|
||||
$SSLStream = New-Object System.Net.Security.SslStream($Client.GetStream(), $false)
|
||||
$SSLStream.AuthenticateAsServer($Certificate, $false, [System.Security.Authentication.SslProtocols]::Tls12, $true) | Out-Null;
|
||||
} catch {
|
||||
Write-IcingaEventMessage -EventId 1500 -Namespace 'Framework' -Objects $Client.Client;
|
||||
return $null;
|
||||
}
|
||||
|
||||
return $SSLStream;
|
||||
}
|
||||
21
lib/web/New-IcingaTCPClient.psm1
Normal file
21
lib/web/New-IcingaTCPClient.psm1
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
function New-IcingaTCPClient()
|
||||
{
|
||||
param(
|
||||
[System.Net.Sockets.TcpListener]$Socket = $null
|
||||
);
|
||||
|
||||
if ($null -eq $Socket) {
|
||||
return $null;
|
||||
}
|
||||
|
||||
[System.Net.Sockets.TcpClient]$Client = $Socket.AcceptTcpClient();
|
||||
|
||||
Write-IcingaDebugMessage -Message (
|
||||
[string]::Format(
|
||||
'New incoming client connection for endpoint {0}',
|
||||
(Get-IcingaTCPClientRemoteEndpoint -Client $Client)
|
||||
)
|
||||
);
|
||||
|
||||
return $Client;
|
||||
}
|
||||
63
lib/web/New-IcingaTCPClientRESTMessage.psm1
Normal file
63
lib/web/New-IcingaTCPClientRESTMessage.psm1
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
function New-IcingaTCPClientRESTMessage()
|
||||
{
|
||||
param(
|
||||
[Hashtable]$Headers = $null,
|
||||
$ContentBody = $null,
|
||||
[int]$HTTPResponse = 200,
|
||||
[switch]$BasicAuth = $FALSE
|
||||
);
|
||||
|
||||
[string]$ContentLength = '';
|
||||
[string]$HTMLContent = '';
|
||||
[string]$AuthHeader = '';
|
||||
|
||||
if ($null -ne $ContentBody) {
|
||||
$json = ConvertTo-Json $ContentBody -Depth 100 -Compress;
|
||||
$bytes = [System.Text.Encoding]::UTF8.GetBytes($json);
|
||||
$HTMLContent = [System.Text.Encoding]::UTF8.GetString($bytes);
|
||||
if ($bytes.Length -gt 0) {
|
||||
$ContentLength = [string]::Format(
|
||||
'Content-Length: {0}{1}',
|
||||
$bytes.Length,
|
||||
(New-IcingaNewLine)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if ($BasicAuth) {
|
||||
$AuthHeader = [string]::Format(
|
||||
'WWW-Authenticate: Basic realm="Icinga for Windows"{0}',
|
||||
(New-IcingaNewLine)
|
||||
);
|
||||
}
|
||||
|
||||
$ResponseMeessage = -Join(
|
||||
[string]::Format(
|
||||
'HTTP/1.1 {0} {1}{2}',
|
||||
$HTTPResponse,
|
||||
$IcingaHTTPEnums.HTTPResponseCode[$HTTPResponse],
|
||||
(New-IcingaNewLine)
|
||||
),
|
||||
[string]::Format(
|
||||
'Server: {0}{1}',
|
||||
(Get-IcingaHostname -LowerCase $TRUE -AutoUseFQDN $TRUE),
|
||||
(New-IcingaNewLine)
|
||||
),
|
||||
[string]::Format(
|
||||
'Content-Type: application/json{0}',
|
||||
(New-IcingaNewLine)
|
||||
),
|
||||
$AuthHeader,
|
||||
$ContentLength,
|
||||
(New-IcingaNewLine),
|
||||
$HTMLContent
|
||||
);
|
||||
|
||||
# Encode our message before sending it
|
||||
$UTF8Message = [System.Text.Encoding]::UTF8.GetBytes($ResponseMeessage);
|
||||
|
||||
return @{
|
||||
'message' = $UTF8Message;
|
||||
'length' = $UTF8Message.Length;
|
||||
};
|
||||
}
|
||||
33
lib/web/New-IcingaTCPSocket.psm1
Normal file
33
lib/web/New-IcingaTCPSocket.psm1
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
function New-IcingaTCPSocket()
|
||||
{
|
||||
param(
|
||||
[int]$Port = 0,
|
||||
[switch]$Start = $FALSE
|
||||
);
|
||||
|
||||
if ($Port -eq 0) {
|
||||
throw 'Please specify a valid port to open a TCP socket for';
|
||||
}
|
||||
|
||||
$TCPSocket = [System.Net.Sockets.TcpListener]$Port;
|
||||
|
||||
Write-IcingaDebugMessage -Message (
|
||||
[string]::Format(
|
||||
'Creating new TCP socket on Port {0}. Endpoint configuration {1}',
|
||||
$Port,
|
||||
$TCPSocket.LocalEndpoint
|
||||
)
|
||||
);
|
||||
|
||||
if ($Start) {
|
||||
Write-IcingaDebugMessage -Message (
|
||||
[string]::Format(
|
||||
'Starting TCP socket for endpoint {0}',
|
||||
$TCPSocket.LocalEndpoint
|
||||
)
|
||||
);
|
||||
$TCPSocket.Start();
|
||||
}
|
||||
|
||||
return $TCPSocket;
|
||||
}
|
||||
18
lib/web/Open-IcingaTCPClientConnection.psm1
Normal file
18
lib/web/Open-IcingaTCPClientConnection.psm1
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
function Open-IcingaTCPClientConnection()
|
||||
{
|
||||
param(
|
||||
[System.Net.Sockets.TcpClient]$Client = $null,
|
||||
[Security.Cryptography.X509Certificates.X509Certificate2]$Certificate = $null
|
||||
);
|
||||
|
||||
if ($null -eq $Client -Or $null -eq $Certificate) {
|
||||
return $null;
|
||||
}
|
||||
|
||||
$Stream = New-IcingaSSLStream -Client $Client -Certificate $Certificate;
|
||||
|
||||
return @{
|
||||
'Client' = $Client;
|
||||
'Stream' = $Stream;
|
||||
};
|
||||
}
|
||||
84
lib/web/Read-IcingaRESTMessage.psm1
Normal file
84
lib/web/Read-IcingaRESTMessage.psm1
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
function Read-IcingaRESTMessage()
|
||||
{
|
||||
param(
|
||||
[string]$RestMessage = $null,
|
||||
[hashtable]$Connection = $null
|
||||
);
|
||||
|
||||
# Just in case we didnt receive anything - no need to
|
||||
# parse through everything
|
||||
if ([string]::IsNullOrEmpty($RestMessage)) {
|
||||
return $null;
|
||||
}
|
||||
|
||||
Write-IcingaDebugMessage (
|
||||
[string]::Format(
|
||||
'Receiving client message{0}{0}{1}',
|
||||
(New-IcingaNewline),
|
||||
$RestMessage
|
||||
)
|
||||
);
|
||||
|
||||
[hashtable]$Request = @{};
|
||||
$RestMessage -match '(.+) (.+) (.+)' | Out-Null;
|
||||
|
||||
$Request.Add('Method', $Matches[1]);
|
||||
$Request.Add('FullRequest', $Matches[2]);
|
||||
$Request.Add('RequestPath', @{});
|
||||
$Request.Add('RequestArguments', @{});
|
||||
|
||||
#Path
|
||||
$PathMatch = $Matches[2];
|
||||
$PathMatch -match '((\/[^\/\?]+)*)\??([^\/]*)' | Out-Null;
|
||||
$Arguments = $Matches[3];
|
||||
$Request.RequestPath.Add('FullPath', $Matches[1]);
|
||||
$Request.RequestPath.Add('PathArray', $Matches[1].TrimStart('/').Split('/'));
|
||||
|
||||
$Matches = $null;
|
||||
|
||||
# Arguments
|
||||
$ArgumentsSplit = $Arguments.Split('&');
|
||||
$ArgumentsSplit+='\\\\\\\\\\\\=FIN';
|
||||
foreach ( $Argument in $ArgumentsSplit | Sort-Object -descending) {
|
||||
if ($Argument.Contains('=')) {
|
||||
$Argument -match '(.+)=(.+)' | Out-Null;
|
||||
If (($Matches[1] -ne $Current) -And ($NULL -ne $Current)) {
|
||||
$Request.RequestArguments.Add( $Current, $ArgumentContent );
|
||||
[array]$ArgumentContent = $null;
|
||||
}
|
||||
$Current = $Matches[1];
|
||||
[array]$ArgumentContent += ($Matches[2]);
|
||||
} else {
|
||||
$Request.RequestArguments.Add( $Argument, $null );
|
||||
}
|
||||
}
|
||||
|
||||
# Header
|
||||
$Request.Add( 'Header', @{} );
|
||||
$SplitString = $RestMessage.split("`r`n");
|
||||
foreach ( $SingleString in $SplitString ) {
|
||||
if ( ([string]::IsNullOrEmpty($SingleString) -eq $FALSE) -And ($SingleString -match '^{.+' -eq $FALSE) ) {
|
||||
$SingleSplitString = $SingleString.Split(':',2);
|
||||
$Request.Header.Add( $SingleSplitString[0], $SingleSplitString[1].Trim());
|
||||
}
|
||||
}
|
||||
|
||||
$Request.Add('ContentLength', [int](Get-IcingaRESTHeaderValue -Header 'Content-Length' -Request $Request));
|
||||
|
||||
$Matches = $null;
|
||||
|
||||
# Body
|
||||
$RestMessage -match '(\{(.*\n)*}|\{.*\})' | Out-Null;
|
||||
$Request.Add('Body', $Matches[1]);
|
||||
|
||||
# We received a content length, but couldnt load the body. Some clients will send the body as separate message
|
||||
# Lets try to read the body content
|
||||
if ($null -ne $Connection) {
|
||||
if ($Request.ContainsKey('ContentLength') -And $Request.ContentLength -gt 0 -And ($Request.ContainsKey('Body') -eq $FALSE -Or [string]::IsNullOrEmpty($Request.Body))) {
|
||||
$Request.Body = Read-IcingaTCPStream -Client $Connection.Client -Stream $Connection.Stream -ReadLength $Request.ContentLength;
|
||||
Write-IcingaDebugMessage -Message 'Body Content' -Objects $Request;
|
||||
}
|
||||
}
|
||||
|
||||
return $Request;
|
||||
}
|
||||
31
lib/web/Read-IcingaTCPStream.psm1
Normal file
31
lib/web/Read-IcingaTCPStream.psm1
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
function Read-IcingaTCPStream()
|
||||
{
|
||||
param(
|
||||
[System.Net.Sockets.TcpClient]$Client = @{},
|
||||
[System.Net.Security.SslStream]$Stream = $null,
|
||||
[int]$ReadLength = 0
|
||||
);
|
||||
|
||||
if ($ReadLength -eq 0) {
|
||||
$ReadLength = $Client.ReceiveBufferSize;
|
||||
}
|
||||
|
||||
if ($null -eq $Stream) {
|
||||
return $null;
|
||||
}
|
||||
|
||||
# Get the maxium size of our buffer
|
||||
[byte[]]$bytes = New-Object byte[] $ReadLength;
|
||||
|
||||
# Read the content of our SSL stream
|
||||
$MessgeSize = $Stream.Read($bytes, 0, $ReadLength);
|
||||
|
||||
Write-IcingaDebugMessage -Message 'Network Stream message size and content in bytes' -Objects $MessgeSize, $bytes;
|
||||
|
||||
# Resize our array to the correct size
|
||||
[byte[]]$resized = New-Object byte[] $MessgeSize;
|
||||
[array]::Copy($bytes, 0, $resized, 0, $MessgeSize);
|
||||
|
||||
# Return our message content
|
||||
return [System.Text.Encoding]::UTF8.GetString($resized);
|
||||
}
|
||||
14
lib/web/Send-IcingaTCPClientMessage.psm1
Normal file
14
lib/web/Send-IcingaTCPClientMessage.psm1
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
function Send-IcingaTCPClientMessage()
|
||||
{
|
||||
param(
|
||||
[Hashtable]$Message = @{},
|
||||
[System.Net.Security.SslStream]$Stream = $null
|
||||
);
|
||||
|
||||
if ($null -eq $Message -Or $Message.Count -eq 0 -Or $Message.length -eq 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
$Stream.Write($Message.message, 0, $Message.length);
|
||||
$Stream.Flush();
|
||||
}
|
||||
Loading…
Reference in a new issue