Adds support for JEA profiles

This commit is contained in:
Lord Hepipud 2021-08-06 18:12:27 +02:00
parent b335a4a588
commit eb8ea9a497
104 changed files with 2454 additions and 384 deletions

8
.gitignore vendored
View file

@ -2,8 +2,14 @@ log/*
agent/*
config/*
cache/*
.vscode/
*.log
# JEA
RoleCapabilities/IcingaForWindows.psrc
IcingaForWindows.pssc
IcingaForWindowsTest.pssc
!cache/README.md
!cache/framework_cache.psm1
!cache/framework_cache.psm1

View file

View file

@ -13,3 +13,6 @@ $Global:Icinga = @{
};
Write-IcingaFrameworkCodeCache;
Import-Module icinga-powershell-framework -Global -Force;
Import-Module icinga-powershell-framework -Force;

48
doc/08-JEA-Profiles.md Normal file
View file

@ -0,0 +1,48 @@
# JEA Profiles
Starting with Icinga for Windows v1.6.0, we are supporting JEA profiles and provide all required tools to build a profile based on installed Icinga for Windows components.
JEA stands for "Just Enough Administration" and you can read more about it on the [Microsoft Docs](https://docs.microsoft.com/de-de/powershell/scripting/learn/remoting/jea/overview).
In short, JEA allows you to limit the access to certain Cmdlets, Functions and Binaries on the system. In addition, you can grant additional privileges to users to perform tasks, which are permitted to Administrators only in general.
With JEA profiles, you can for example grant permission to certain users or group to restart a specific service, after starting a PowerShell with a specific JEA profile. You can limit the access only to this command to be executed in this elevated environment, while all other commands or services are still not manageable.
## Requirements
In order to use JEA profiles, you will require the following system requirements:
* PowerShell 5.0 or later
* WinRM service configured
## Why use JEA for Icinga for Windows
Using JEA profiles will increase security in a certain way, while also ensuring that you no longer have to manage certain permissions for the monitoring user account. Instead of granting permissions to certain services, WMI objects or anything related, each command is executed within the `System` context. By defining profiles, you can ensure that fetching of these information is possible, but not modifying the system itself.
For monitoring for example, certain `Scheduled Tasks` or even `Services` are not accessible by some users. To fetch the `vmms` service for Hyper-V for example, you need either to execute the checks in the context of `Hyper-V Administrators` or `LocalSystem`. Both are then unrestricted on how they can interact with Hyper-V, causing a possible security gap.
## What can Icinga for Windows JEA and what can't it do
Icinga for Windows provides `Cmdlets`, to automatically build a JEA profile based on your installed Icinga for Windows components. Each single used `Cmldet` is being analyzed and checked for commands being executed, to ensure plugins have access to all required tools to properly execute them and return the plugin information.
### No hundred percent security
By default, Icinga for Windows JEA profiles are created with the PowerShell language mode `FullLanguage`. This in general allows the execution of `ScriptBlocks` and other non-blocked Cmdlets, while `ConstrainedLanguage` is more restrictive on which commands can be executed by default, prohibiting `ScriptBlocks` and modifying `global variables` later on.
If Icinga for Windows is used with the Icinga for Windows service, the `ConstrainedLanguage` flag will cause the the service to not work, as the service relies within the started PowerShell session to modify `global variables`, which is impossible in this mode. During development, we started to get rid of `ScriptBlocks` and user other methods for creating the internal threads.
### No ScriptBlocks allowed
Starting a JEA session with `FullLanguage`, will ensure that you can only execute commands you are permitted for. Any other command is not available and will be blocked. However, this changes once you create a `ScriptBlock`, because these will execute commands even when you should not be permitted to execute them. To mitigate this problem, Icinga for Windows will not add any command or module which ships with `ScriptBlocks` inside.
### Increase Icinga for Windows security
For better security, it is highly recommended to install the `Icinga PowerShell Framework` inside a context, that requires administrative privileges for making changes. By default, this would for example be `C:\Program Files\WindowsPowerShell\Modules\`.
The JEA profile generator will lookup the root folder, in which the `Icinga PowerShell Framework` is installed into and only lookup Icinga for Windows components installed there. Any other Icinga for Windows module installed on the system is not included.
This will ensure that you will require administrative privileges beforehand to modify these files, to later execute them inside the JEA context.
## Getting Started
To get started with the Icinga for Windows JEA profile, have a look on the [installation guide](jea/01-Installation.md).

View file

@ -433,7 +433,7 @@ Register-IcingaBackgroundDaemon -Command 'Start-IcingaAgentServiceTest';
Once registered, you will have to restart the PowerShell service itselfs to apply the changes
```powershell
Restart-IcingaService 'icingapowershell';
Restart-IcingaWindowsService;
```
Thats it! Now the daemon is loaded with every start, checking for the Agent state and restart it if it is not running.

View file

@ -229,7 +229,7 @@ If our module is providing different endpoints, you will have to create multiple
As everything is now ready, we can restart our Icinga PowerShell Framework service by using
```powershell
Restart-IcingaService 'icingapowershell';
Restart-IcingaWindowsService;
```
and access our API endpoint by browsing to our API location (in our example we assume you use `5668` as default port):

View file

@ -54,7 +54,7 @@ To modify any REST-Api arguments, please follow the [REST-Api installation guide
Last but not least restart the Icinga for Windows service:
```powershell
Restart-Service icingapowershell;
Restart-IcingaWindowsService;
```
## Whitelist Check Commands
@ -112,9 +112,9 @@ Install-IcingaFrameworkComponent -Name restapi -Release;
Install-IcingaFrameworkComponent -Name apichecks -Release;
Register-IcingaBackgroundDaemon -Command 'Start-IcingaWindowsRESTApi';
Restart-Service icingapowershell;
Add-IcingaRESTApiCommand -Command 'Invoke-IcingaCheck*' -Endpoint 'apichecks';
Restart-IcingaWindowsService;
Enable-IcingaFrameworkApiChecks;
```

View file

@ -0,0 +1,98 @@
# Install JEA
## Preparations
Before we can use JEA profiles, we require to prepare our Windows machine for this. JEA profiles require a configured and running `WinRM` service, allowing PowerShell remove executions.
The simple and easiest way, is to enable it with `Enable-PSRemoting`.
**NOTE:** Please check your local security profiles and configurations before applying these changes. This installation step is not focussing on how to secure `WinRM` in your environment and just gives an example on how you can get started. You are responsible for yourself to properly secure `WinRM`, depending on your environment.
Once `WinRM` is enabled and properly configured on your system, you can move on with installing the Icinga for Windows JEA profile
## Install Icinga for Windows JEA profile
We provide two ways on how Icinga for Windows is configured and JEA profiles are build. The easiest and most straight forward solution, is creating an own user which is managed by `Icinga for Windows` on the local system. The other option is to manually assign a user and create the profile this one.
### JEA with Icinga for Windows managed User
To fully automate the entire process and to ensure Icinga for Windows is executed with a dedicated user we run our JEA profile with, we can simply use the command `Install-IcingaSecurity`.
This command will
* install a user called `icinga` on the system
* create a JEA profile for this user
You can modify the name of the user with the `-IcingaUser` argument, to create a managed user with a different name.
```powershell
Install-IcingaSecurity -IcingaUser 'MyOwnIcingaUser';
```
The user created by this command is not added to any user group and is only permitted to be used as service user. Local logins or RDP sessions are not forbidden.
The user is created with a random, 60 digits password to ensure security. Each time the service is being modified with the user, the password is randomly re-created to ensure a valid login of the service user. The password is not stored anywhere on the Icinga for Windows context, besides the PowerShell session which is executed. However, once all actions the password is required for are completed, the variable is flushed from the memory.
If present, both services `icinga2` and `icingapowershell` are updated to use the newly created user and being restarted afterwards.
Once completed, Icinga for Windows will compile the JEA profile with the name `IcingaForWindows`.
### JEA with non-managed user
If you already use a monitoring user and create a user automatically, you can simply use `Install-IcingaJEAProfile`, by providing the user the profile is created for. The default user is set to `IcingaForWindows`, but can be overwritten.
```powershell
Install-IcingaJEAProfile -IcingaUser 'MyOwnIcingaUser';
```
This will create the JEA profile files and register them, but not modify any services or user data.
## Additional Management
There are additional arguments available for `Install-IcingaJEAProfile`, which can be used to change the behaviour a little.
| Argument | Type | Description |
| --- | --- | --- |
| IcingaUser | String | The name of the user the JEA profile is created for |
| ConstrainedLanguage | Switch | Will create the JEA profile with language mode `ConstrainedLanguage` instead of `FullLanguage`, for increased security. Please note that the `Icinga for Windows service` will not work with this configuration |
| TestEnv | Switch | By enabling this flag, a second JEA test profile is created for the current using running the PowerShell for testing purpose. The profile is called `IcingaForWindowsTest` |
## Creating Test Environment with existing Profile
If you already created a profile with `Install-IcingaJEAProfile`, you can simply register a test environment for the current user, not requiring a full-rebuild of the JEA profile.
```powershell
Register-IcingaJEAProfile -TestEnv
```
`Register-IcingaJEAProfile` supports the same arguments as listed above for `Install-IcingaJEAProfile`.
## Update JEA Profiles
To update your JEA profiles after you updated components or made modifications for yourself, you can rebuild the profile by using `Install-IcingaJEAProfile` with any of the above mentioned arguments or use the alias `Update-IcingaJEAProfile`, which does the same and is just named differently.
```powershell
Update-IcingaJEAProfile -IcingaUser 'MyOwnIcingaUser';
```
## Use JEA profile
### Use test environment JEA
If you used `TestEnv` to create a test environment for JEA for the current user, you can simply enter the PowerShell JEA session with this command:
```powershell
powershell.exe -ConfigurationName 'IcingaForWindowsTest';
```
This will open a new `remote` PowerShell session over `WinRM` on the local machine with the provided JEA profile 'IcingaForWindowsTest'.
### Apply JEA to Icinga configuration
Each plugin bundle shipped by the Icinga Team has new configuration `baskets` for the Icinga Director and `conf` files for Icinga 2 compiled with a new argument `-JEAProfile`.
To make sure the Icinga Agent will execute plugins with the Icinga for Windows JEA context, you will have to add this to your CheckCommand or Service templates.
The profile we create is called `IcingaForWindows` and can simply added to the CheckCommand definition for global rollout.
**Note:** If you add this configuration in Icinga globally, each single node will fail it's checks if the JEA profile is not installed there.

View file

@ -0,0 +1,31 @@
# Uninstall JEA
If you want to uninstall JEA profiles or even the managed user included, there are `Cmdlets` available for this.
## Uninstall JEA with managed user
To uninstall JEA profiles and the managed user, you can use `Uninstall-IcingaSecurity`. Like the installation counterpart, you can specify a custom user with `-IcingaUser`.
However, users will only be removed if their description matches the Icinga for Windows managed user description.
```powershell
Uninstall-IcingaSecurity -IcingaUser 'MyOwnIcingaUser';
```
By default, it will remove the `icinga` user including unregistering the JEA profile.
## Uninstall JEA Profile
To simply uninstall the JEA profile and leave a possible managed user on the system, you can run `Uninstall-IcingaJEAProfile`.
This will remove the created JEA profile and the JEA catalog for `IcingaForWindows` on the system.
## Uninstall JEA test profile
To simply remove the test environment of the JEA profile, you can use this command:
```powershell
Unregister-PSSessionConfiguration -Name 'IcingaForWindowsTest';
```
This will leave the catalog and the production system itself alone and only removes the test profile.

View file

@ -83,7 +83,7 @@ Now open a new PowerShell session again and check of the new directory was added
Once the directory is there, restart the `icingapowershell` service by running
```powershell
Restart-Service 'icingapowershell'
Restart-IcingaWindowsService;
```
Now the error should be resolved the the service should be running.
@ -119,7 +119,7 @@ Now open a new PowerShell session again and check of the new directory was added
Once the directory is there, restart the `icingapowershell` service by running
```powershell
Restart-Service 'icingapowershell'
Restart-IcingaWindowsService;
```
Now the error should be resolved the the service should be running.

View file

@ -17,7 +17,7 @@ The `Start-IcingaServiceCheckDaemon` is a directly integrated PowerShell functio
Once you made changes, please remember to restart the PowerShell Service
```powershell
Restart-IcingaService 'icingapowershell';
Restart-IcingaWindowsService;
```
List Enabled Daemons
@ -49,7 +49,7 @@ Unregister-IcingaBackgroundDaemon -BackgroundDaemon 'Start-IcingaServiceCheckDae
Once you restart the PowerShell service the pending changes are applied
```powershell
Restart-IcingaService 'icingapowershell';
Restart-IcingaWindowsService;
```
Write Custom Daemons

View file

@ -17,7 +17,7 @@ Register-IcingaServiceCheck -CheckCommand 'Invoke-IcingaCheckCPU' -Interval 30 -
Once you registered a service check, you will have to restart the PowerShell service
```powershell
Restart-IcingaService 'icingapowershell';
Restart-IcingaWindowsService;
```
As collected metrics are written to disk as well, a restart will not flush previous data but load it again. A quick service restart will not cause missing data points.
@ -66,7 +66,7 @@ Register-IcingaServiceCheck -CheckCommand 'Invoke-IcingaCheckCPU' -Interval 60 -
Once you modified a service check, you will have to restart the PowerShell service
```powershell
Restart-IcingaService 'icingapowershell';
Restart-IcingaWindowsService;
```
Unregister Service Checks
@ -81,5 +81,5 @@ Unregister-IcingaServiceCheck -ServiceId 527521986464102122481142022477689145963
Once you removed a service check, you will have to restart the PowerShell service
```powershell
Restart-IcingaService 'icingapowershell';
Restart-IcingaWindowsService;
```

View file

@ -10,7 +10,7 @@
NestedModules = @( '.\cache\framework_cache.psm1' )
FunctionsToExport = @( '*' )
CmdletsToExport = @( '*' )
VariablesToExport = '*'
VariablesToExport = @( '*' )
AliasesToExport = @( '*' )
PrivateData = @{
PSData = @{

View file

@ -35,8 +35,6 @@ function Use-Icinga()
if ($global:Icinga.ContainsKey('Minimal') -eq $FALSE) {
$global:Icinga.Add('Minimal', $TRUE);
}
return;
}
# Ensure we autoload the Icinga Plugin collection, provided by the external
@ -46,10 +44,10 @@ function Use-Icinga()
}
if ($LibOnly -eq $FALSE) {
$global:IcingaThreads = [hashtable]::Synchronized(@{});
$global:IcingaThreadContent = [hashtable]::Synchronized(@{});
$global:IcingaThreadPool = [hashtable]::Synchronized(@{});
$global:IcingaTimers = [hashtable]::Synchronized(@{});
$global:IcingaThreads = [hashtable]::Synchronized(@{ });
$global:IcingaThreadContent = [hashtable]::Synchronized(@{ });
$global:IcingaThreadPool = [hashtable]::Synchronized(@{ });
$global:IcingaTimers = [hashtable]::Synchronized(@{ });
$global:IcingaDaemonData = [hashtable]::Synchronized(
@{
'IcingaThreads' = $global:IcingaThreads;
@ -57,6 +55,7 @@ function Use-Icinga()
'IcingaThreadPool' = $global:IcingaThreadPool;
'IcingaTimers' = $global:IcingaTimers;
'FrameworkRunningAsDaemon' = $Daemon;
'JEAContext' = $FALSE;
'DebugMode' = $DebugMode;
}
);
@ -64,11 +63,14 @@ function Use-Icinga()
# 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(@{});
$global:IcingaDaemonData = [hashtable]::Synchronized(@{ });
}
if ($global:IcingaDaemonData.ContainsKey('DebugMode') -eq $FALSE) {
$global:IcingaDaemonData.DebugMode = $DebugMode;
}
if ($global:IcingaDaemonData.ContainsKey('JEAContext') -eq $FALSE) {
$global:IcingaDaemonData.JEAContext = $FALSE;
}
if ($global:IcingaDaemonData.ContainsKey('FrameworkRunningAsDaemon') -eq $FALSE) {
$global:IcingaDaemonData.FrameworkRunningAsDaemon = $Daemon;
}
@ -121,6 +123,8 @@ function Write-IcingaFrameworkCodeCache()
$CacheContent += "Export-ModuleMember -Function @( '*' ) -Alias @( '*' ) -Variable @( '*' )";
Set-Content -Path $CacheFile -Value $CacheContent;
Remove-IcingaFrameworkDependencyFile;
}
function Publish-IcingaEventlogDocumentation()

View file

@ -21,10 +21,10 @@
function Test-IcingaPowerShellConfigItem()
{
param(
param (
$ConfigObject,
$ConfigKey
);
return ([bool]($ConfigObject.PSobject.Properties.Name -eq $ConfigKey) -eq $TRUE);
return ([bool]($ConfigObject.PSObject.Properties.Name -eq $ConfigKey) -eq $TRUE);
}

View file

@ -0,0 +1,37 @@
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-Output $DocContent;
} else {
Write-IcingaFileSecure -File $OutFile -Value $DocContent;
}
}

View file

@ -47,11 +47,13 @@ function Install-IcingaForWindowsService()
if ($ServiceStatus -eq 'Running') {
Write-IcingaConsoleNotice 'Stopping Icinga PowerShell service';
Stop-IcingaService 'icingapowershell';
Stop-IcingaWindowsService;
Start-Sleep -Seconds 1;
}
Remove-ItemSecure -Path $Path -Force | Out-Null;
if (Test-Path $Path) {
Remove-ItemSecure -Path $Path -Force | Out-Null;
}
Copy-ItemSecure -Path $UpdateFile -Destination $Path -Force | Out-Null;
Remove-ItemSecure -Path $UpdateFile -Force | Out-Null;
}
@ -73,7 +75,11 @@ function Install-IcingaForWindowsService()
throw ([string]::Format('Failed to install Icinga PowerShell Service: {0}{1}', $ServiceCreation.Message, $ServiceCreation.Error));
}
} else {
Write-IcingaConsoleWarning 'The Icinga PowerShell Service is already installed';
$ServiceUpdate = Start-IcingaProcess -Executable 'sc.exe' -Arguments ([string]::Format('config icingapowershell binPath= "{0}"', $Path));
if ($ServiceUpdate.ExitCode -ne 0) {
throw ([string]::Format('Failed to update config for Icinga PowerShell Service: {0}{1}', $ServiceUpdate.Message, $ServiceUpdate.Error));
}
}
# This is just a hotfix to ensure we setup the service properly before assigning it to
@ -81,9 +87,9 @@ function Install-IcingaForWindowsService()
# will not start without this workaround.
# Todo: Figure out the reason and fix it properly
Set-IcingaAgentServiceUser -User 'LocalSystem' -Service 'icingapowershell' | Out-Null;
Restart-IcingaService 'icingapowershell';
Restart-IcingaWindowsService;
Start-Sleep -Seconds 1;
Stop-IcingaService 'icingapowershell';
Stop-IcingaWindowsService;
if ($ServiceStatus -eq 'Running') {
Write-IcingaConsoleNotice 'Starting Icinga PowerShell service';

View file

@ -110,6 +110,11 @@ function Install-IcingaFrameworkComponent()
# include the plugins
Use-Icinga;
if ([string]::IsNullOrEmpty((Get-IcingaJEAContext)) -eq $FALSE) {
Write-IcingaConsoleNotice 'Updating Icinga JEA profile';
Invoke-IcingaCommand { Install-IcingaJEAProfile };
}
# Unload the module if it was loaded before
Remove-Module $PluginDirectory -Force -ErrorAction SilentlyContinue;
# Now import the module

View file

@ -57,7 +57,7 @@ function Install-IcingaFrameworkUpdate()
if ($ServiceStatus -eq 'Running') {
Write-IcingaConsoleNotice 'Stopping Icinga PowerShell service';
Stop-IcingaService 'icingapowershell';
Stop-IcingaWindowsService;
Start-Sleep -Seconds 1;
}
if ($AgentStatus -eq 'Running') {
@ -78,12 +78,11 @@ function Install-IcingaFrameworkUpdate()
Write-IcingaConsoleNotice 'Removing files from framework';
foreach ($ModuleFile in $Files) {
Remove-ItemSecure -Path $ModuleFile -Force | Out-Null;
Remove-ItemSecure -Path $ModuleFile.FullName -Force | Out-Null;
}
Remove-ItemSecure -Path (Join-Path $ModuleDirectory -ChildPath 'doc') -Recurse -Force | Out-Null;
Remove-ItemSecure -Path (Join-Path $ModuleDirectory -ChildPath 'lib') -Recurse -Force | Out-Null;
Remove-ItemSecure -Path (Join-Path $ModuleDirectory -ChildPath 'manifests') -Recurse -Force | Out-Null;
Write-IcingaConsoleNotice 'Copying new files to framework';
Copy-ItemSecure -Path (Join-Path $ModuleContent -ChildPath 'doc') -Destination $ModuleDirectory -Recurse -Force | Out-Null;
@ -102,6 +101,12 @@ function Install-IcingaFrameworkUpdate()
Write-IcingaFrameworkCodeCache;
}
if ([string]::IsNullOrEmpty((Get-IcingaJEAContext)) -eq $FALSE) {
Remove-IcingaFrameworkDependencyFile;
Write-IcingaConsoleNotice 'Updating Icinga JEA profile';
Invoke-IcingaCommand { Install-IcingaJEAProfile };
}
Write-IcingaConsoleNotice 'Framework update has been completed. Please start a new PowerShell instance now to complete the update';
Test-IcingaForWindowsService -ResolveProblems | Out-Null;

View file

@ -36,7 +36,7 @@ function Test-IcingaZipBinaryChecksum()
[string]$MD5Checksum = Get-Content $MD5Path;
$MD5Checksum = ($MD5Checksum.Split(' ')[0]).ToLower();
$FileHash = ((Get-FileHash $Path -Algorithm MD5).Hash).ToLower();
$FileHash = ((Get-IcingaFileHash $Path -Algorithm MD5).Hash).ToLower();
if ($MD5Checksum -ne $FileHash) {
return $FALSE;

View file

@ -11,6 +11,9 @@
Uninstalls every PowerShell module within the icinga-powershell-* namespace
including the Icinga Agent with all components (like certificates) as well as
the Icinga for Windows service and the Icinga PowerShell Framework.
.PARAMETER IcingaUser
In case the Icinga Security profile was installed with a defined user any other than
"icinga", you require to specify the user to remove it entirely
.PARAMETER Force
Suppress the question if you are sure to uninstall everything
.INPUTS
@ -24,6 +27,7 @@
function Uninstall-IcingaForWindows()
{
param (
$IcingaUser = 'icinga',
[switch]$Force = $FALSE
);
@ -45,6 +49,8 @@ function Uninstall-IcingaForWindows()
}
Write-IcingaConsoleNotice 'Uninstalling Icinga for Windows from this host';
Write-IcingaConsoleNotice 'Uninstalling Icinga Security configuration if applied';
Uninstall-IcingaSecurity -IcingaUser $IcingaUser;
Write-IcingaConsoleNotice 'Uninstalling Icinga Agent';
Uninstall-IcingaAgent -RemoveDataFolder | Out-Null;
Write-IcingaConsoleNotice 'Uninstalling Icinga for Windows service';

View file

@ -24,7 +24,7 @@ function Uninstall-IcingaForWindowsService()
$ServiceData = Get-IcingaForWindowsServiceData;
Stop-IcingaService 'icingapowershell';
Stop-IcingaWindowsService;
Start-Sleep -Seconds 1;
$ServiceCreation = Start-IcingaProcess -Executable 'sc.exe' -Arguments 'delete icingapowershell';

View file

@ -1,5 +1,13 @@
function Get-IcingaAgentHostCertificate()
{
if (-Not (Test-Path -Path (Join-Path -Path $Env:ProgramData -ChildPath 'icinga2\var\lib\icinga2\certs\'))) {
return @{
'CertFile' = '';
'Subject' = '';
'Thumbprint' = '';
};
}
# 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';

View file

@ -2,7 +2,10 @@ function Get-IcingaServiceUser()
{
$Services = Get-IcingaServices -Service 'icinga2';
if ($null -eq $Services) {
throw 'Icinga Service not installed';
$Services = Get-IcingaServices -Service 'icingapowershell';
if ($null -eq $Services) {
return $null;
}
}
$Services = $Services.GetEnumerator() | Select-Object -First 1;

View file

@ -749,7 +749,7 @@ function Start-IcingaAgentInstallWizard()
}
Test-IcingaAgent;
if ($InstallFrameworkService) {
Restart-IcingaService 'icingapowershell';
Restart-IcingaWindowsService;
}
Restart-IcingaService 'icinga2';
}

View file

@ -1,24 +1,38 @@
function Set-IcingaAcl()
{
param(
[string]$Directory
[string]$Directory,
[string]$IcingaUser = (Get-IcingaServiceUser),
[switch]$Remove = $FALSE
);
if (-Not (Test-Path $Directory)) {
throw 'Failed to set Acl for directory. Directory does not exist';
Write-IcingaConsoleWarning 'Unable to set ACL for directory "{0}". Directory does not exist' -Objects $Directory;
return;
}
$DirectoryAcl = (Get-Item -Path $Directory).GetAccessControl('Access');
$DirectoryAccessRule = New-Object System.Security.AccessControl.FileSystemAccessRule(
(Get-IcingaServiceUser),
$IcingaUser,
'Modify',
'ContainerInherit,ObjectInherit',
'None',
'Allow'
);
$DirectoryAcl.SetAccessRule($DirectoryAccessRule);
if ($Remove -eq $FALSE) {
$DirectoryAcl.SetAccessRule($DirectoryAccessRule);
} else {
foreach ($entry in $DirectoryAcl.Access) {
if (([string]($entry.IdentityReference)).ToLower() -like [string]::Format('*\{0}', $IcingaUser.ToLower())) {
$DirectoryAcl.RemoveAccessRuleSpecific($entry);
}
}
}
Set-Acl -Path $Directory -AclObject $DirectoryAcl;
Test-IcingaAcl -Directory $Directory -WriteOutput | Out-Null;
if ($Remove -eq $FALSE) {
Test-IcingaAcl -Directory $Directory -WriteOutput | Out-Null;
}
}

View file

@ -1,4 +1,4 @@
function Set-IcingaAgentServiceUser()
function Set-IcingaServiceUser()
{
param (
[string]$User,
@ -12,6 +12,10 @@ function Set-IcingaAgentServiceUser()
return $FALSE;
}
if ($null -eq (Get-Service $Service -ErrorAction SilentlyContinue)) {
return;
}
if ($User.Contains('\') -eq $FALSE) {
$User = [string]::Format('.\{0}', $User);
}
@ -29,13 +33,16 @@ function Set-IcingaAgentServiceUser()
if ($Output.ExitCode -eq 0) {
if ($SetPermission) {
Set-IcingaAgentServicePermission | Out-Null;
Set-IcingaUserPermissions;
}
Write-IcingaConsoleNotice 'Service User successfully updated'
Write-IcingaConsoleNotice 'Service User "{0}" for service "{1}" successfully updated' -Objects $User, $Service;
return $TRUE;
} else {
Write-IcingaConsoleError ([string]::Format('Failed to update the service user: {0}', $Output.Message));
return $FALSE;
}
}
Set-Alias -Name 'Set-IcingaAgentServiceUser' -Value 'Set-IcingaServiceUser';

View file

@ -1,7 +1,12 @@
function Set-IcingaUserPermissions()
{
Set-IcingaAgentServicePermission | Out-Null;
Set-IcingaAcl "$Env:ProgramData\icinga2\etc";
Set-IcingaAcl "$Env:ProgramData\icinga2\var";
Set-IcingaAcl (Get-IcingaCacheDir);
param (
[string]$IcingaUser = (Get-IcingaServiceUser),
[switch]$Remove = $FALSE
);
Set-IcingaAcl "$Env:ProgramData\icinga2\etc" -IcingaUser $IcingaUser -Remove:$Remove;
Set-IcingaAcl "$Env:ProgramData\icinga2\var" -IcingaUser $IcingaUser -Remove:$Remove;
Set-IcingaAcl (Get-IcingaCacheDir) -IcingaUser $IcingaUser -Remove:$Remove;
Set-IcingaAcl -Directory (Get-IcingaPowerShellConfigDir) -IcingaUser $IcingaUser -Remove:$Remove;
}

View file

@ -2,15 +2,16 @@ function Test-IcingaAcl()
{
param(
[string]$Directory,
[switch]$WriteOutput
[switch]$WriteOutput,
[string]$ServiceUser = (Get-IcingaServiceUser)
);
if ([string]::IsNullOrEmpty($Directory) -Or -Not (Test-Path $Directory)) {
throw 'The specified directory was not found';
Write-IcingaConsoleWarning 'The specified directory "{0}" was not found' -Objects $Directory;
return $FALSE;
}
$FolderACL = Get-Acl $Directory;
$ServiceUser = Get-IcingaServiceUser;
$UserFound = $FALSE;
$HasAccess = $FALSE;
$ServiceUserSID = Get-IcingaUserSID $ServiceUser;

View file

@ -1,18 +1,30 @@
function Test-IcingaAgent()
{
if (Get-Service 'icinga2' -ErrorAction SilentlyContinue) {
$IcingaAgentData = Get-IcingaAgentInstallation;
$AgentServicePresent = Get-Service 'icinga2' -ErrorAction SilentlyContinue;
if ($IcingaAgentData.Installed -And $null -ne $AgentServicePresent) {
Write-IcingaTestOutput -Severity 'Passed' -Message 'Icinga Agent service is installed';
Test-IcingaAgentServicePermission | Out-Null;
Test-IcingaAcl "$Env:ProgramData\icinga2\etc" -WriteOutput | Out-Null;
Test-IcingaAcl "$Env:ProgramData\icinga2\var" -WriteOutput | Out-Null;
Test-IcingaAcl (Get-IcingaCacheDir) -WriteOutput | Out-Null;
} elseif ($IcingaAgentData.Installed -And $null -eq $AgentServicePresent) {
Write-IcingaTestOutput -Severity 'Failed' -Message 'Icinga Agent service is not installed';
} elseif ($IcingaAgentData.Installed -eq $FALSE -And $null -ne $AgentServicePresent) {
Write-IcingaTestOutput -Severity 'Failed' -Message 'Icinga Agent service is still present, while Icinga Agent itself is not installed.';
} elseif ($IcingaAgentData.Installed -eq $FALSE -And $null -eq $AgentServicePresent) {
Write-IcingaTestOutput -Severity 'Passed' -Message 'Icinga Agent is not installed and service is not present.';
}
Test-IcingaAgentServicePermission | Out-Null;
Test-IcingaAcl "$Env:ProgramData\icinga2\etc" -WriteOutput | Out-Null;
Test-IcingaAcl "$Env:ProgramData\icinga2\var" -WriteOutput | Out-Null;
Test-IcingaAcl (Get-IcingaCacheDir) -WriteOutput | Out-Null;
Test-IcingaAcl (Get-IcingaPowerShellConfigDir) -WriteOutput | Out-Null;
Test-IcingaAcl -Directory (Join-Path -Path (Get-IcingaFrameworkRootPath) -ChildPath 'certificate') -WriteOutput | Out-Null;;
if ($IcingaAgentData.Installed) {
Test-IcingaAgentConfig | Out-Null;
if (Test-IcingaAgentFeatureEnabled -Feature 'debuglog') {
Write-IcingaTestOutput -Severity 'Warning' -Message 'The debug log of the Icinga Agent is enabled. Please keep in mind to disable it once testing is done, as a huge amount of data is generated'
} else {
Write-IcingaTestOutput -Severity 'Passed' -Message 'Icinga Agent debug log is disabled'
}
} else {
Write-IcingaTestOutput -Severity 'Failed' -Message 'Icinga Agent service is not installed';
}
}

View file

@ -21,6 +21,7 @@ function Install-Icinga()
'DirectorRegisteredHost' = $FALSE;
'LastParent' = [System.Collections.ArrayList]@();
'LastValues' = @();
'DisabledEntries' = @{ };
'Config' = @{ };
'ConfigSwap' = @{ };
'ParentConfig' = $null;

View file

@ -61,7 +61,7 @@ function Start-IcingaForWindowsInstallation()
$PluginPackageSnapshot = $FALSE;
if ([string]::IsNullOrEmpty($IcingaStableRepo) -eq $FALSE) {
Add-IcingaRepository -Name 'Icinga Stable' -RemotePath $IcingaStableRepo;
Add-IcingaRepository -Name 'Icinga Stable' -RemotePath $IcingaStableRepo -Force;
}
foreach ($endpoint in $IcingaEndpoints) {
@ -222,7 +222,7 @@ function Start-IcingaForWindowsInstallation()
}
if ($InstallService) {
Restart-IcingaService 'icingapowershell';
Restart-IcingaWindowsService;
}
switch ($InstallJEAProfile) {

View file

@ -8,8 +8,6 @@ function Show-IcingaForWindowsManagementConsoleInstallationDirectorRegisterHost(
[switch]$Advanced = $FALSE
);
$Advanced = $TRUE;
Show-IcingaForWindowsInstallerMenu `
-Header 'Do you want to register the host right now inside the Icinga Director? This will show missing configurations.' `
-Entries @(
@ -21,7 +19,7 @@ function Show-IcingaForWindowsManagementConsoleInstallationDirectorRegisterHost(
@{
'Caption' = 'Register host inside Icinga Director';
'Command' = 'Show-IcingaForWindowsInstallerConfigurationSummary';
'Help' = 'You can select this option to register the host within the Icinga Director right now, unlocking more advanced configurations for this host like "Parent Zone", "Parent Nodes" and "Parent Node Addresses"';
'Help' = 'You can select this option to register the host within the Icinga Director right now, unlocking more advanced configurations for this host like "Parent Zone", "Parent Nodes" and "Parent Node Addresses". Please note that the installation process will fail if you continue the installer, if you do not register it.';
'Action' = @{
'Command' = 'Resolve-IcingaForWindowsManagementConsoleInstallationDirectorTemplate';
'Arguments' = @{

View file

@ -1,7 +1,7 @@
function Show-IcingaForWindowsInstallationMenuStableRepository()
{
param (
[array]$Value = @( 'https://packages.icinga.com/IcingaForWindows/stable' ),
[array]$Value = @( 'https://packages.icinga.com/IcingaForWindows/stable/ifw.repo.json' ),
[string]$DefaultInput = 'c',
[switch]$JumpToSummary = $FALSE,
[switch]$Automated = $FALSE,
@ -13,7 +13,7 @@ function Show-IcingaForWindowsInstallationMenuStableRepository()
-Entries @(
@{
'Command' = 'Show-IcingaForWindowsInstallerConfigurationSummary';
'Help' = 'This is the stable repository from where all packages of Icinga for Windows are downloaded and installed from. Defaults to "https://packages.icinga.com/IcingaForWindows/stable"';
'Help' = 'This is the stable repository from where all packages of Icinga for Windows are downloaded and installed from. Defaults to "https://packages.icinga.com/IcingaForWindows/stable/ifw.repo.json"';
}
) `
-DefaultIndex $DefaultInput `

View file

@ -8,8 +8,8 @@ function Show-IcingaForWindowsInstallerMenuSelectInstallJEAProfile()
[switch]$Advanced = $FALSE
);
if ($PSVersionTable.PSVersion -lt '5.0.0.0') {
return;
if ($PSVersionTable.PSVersion -lt (New-IcingaVersionObject -Version 5, 0)) {
Add-IcingaForWindowsInstallerDisabledEntry -Name 'IfW-InstallJEAProfile' -Reason ([string]::Format('PowerShell version "{0}" is lower than 5.0', $PSVersionTable.PSVersion.ToString(2)));
}
Show-IcingaForWindowsInstallerMenu `
@ -21,9 +21,9 @@ function Show-IcingaForWindowsInstallerMenuSelectInstallJEAProfile()
'Help' = 'Installs the Icinga for Windows JEA profile for the specified service user';
},
@{
'Caption' = 'Install JEA Profile with managed user "IcingaForWindows"';
'Caption' = 'Install JEA Profile with managed user "icinga"';
'Command' = 'Show-IcingaForWindowsInstallerConfigurationSummary';
'Help' = 'Installs the Icinga for Windows JEA profile with a newly created, managed user "IcingaForWindows". This will override your service and service password configuration';
'Help' = 'Installs the Icinga for Windows JEA profile with a newly created, managed user "icinga". This will override your service and service password configuration';
},
@{
'Caption' = 'Do not install JEA Profile';

View file

@ -78,11 +78,21 @@ function Show-IcingaForWindowsInstallerConfigurationSummary()
$Caption = ([string]::Format('{0}=> {1}', $PrintName, $EntryValue));
}
[bool]$EntryDisabled = $FALSE;
[string]$EntryDisabledReason = Get-IcingaForWindowsInstallerDisabledEntry -Name $RealCommand;
if ([string]::IsNullOrEmpty($EntryDisabledReason) -eq $FALSE) {
$EntryDisabled = $TRUE;
$Caption = ([string]::Format('{0}=> Disabled: {1}', $PrintName, $EntryDisabledReason));
}
$Entries += @{
'Caption' = $Caption;
'Command' = $entry;
'Arguments' = @{ '-JumpToSummary' = $TRUE };
'Help' = ''
'Caption' = $Caption;
'Command' = $entry;
'Arguments' = @{ '-JumpToSummary' = $TRUE };
'Help' = '';
'Disabled' = $EntryDisabled;
'DisabledReason' = $EntryDisabledReason
}
$global:Icinga.InstallWizard.HeaderPreview = '';
@ -91,8 +101,6 @@ function Show-IcingaForWindowsInstallerConfigurationSummary()
$CurrentIndex += 1;
}
Write-Host 'Finished'
Disable-IcingaForWindowsInstallationHeaderPrint;
Enable-IcingaForWindowsInstallationJumpToSummary;

View file

@ -1,9 +1,14 @@
function Show-IcingaForWindowsManagementConsoleManageFramework()
{
$FrameworkDebug = Get-IcingaFrameworkDebugMode;
$IcingaService = Get-Service 'icingapowershell' -ErrorAction SilentlyContinue;
$AdminShell = $global:Icinga.InstallWizard.AdminShell;
$ServiceStatus = $null;
$FrameworkDebug = Get-IcingaFrameworkDebugMode;
$IcingaService = Get-Service 'icingapowershell' -ErrorAction SilentlyContinue;
$AdminShell = $global:Icinga.InstallWizard.AdminShell;
$ServiceStatus = $null;
$JEADisabled = $FALSE;
if ($PSVersionTable.PSVersion -lt (New-IcingaVersionObject -Version 5, 0) -Or $AdminShell -eq $FALSE) {
$JEADisabled = $TRUE;
}
if ($null -ne $IcingaService) {
$ServiceStatus = $IcingaService.Status;
@ -13,24 +18,31 @@ function Show-IcingaForWindowsManagementConsoleManageFramework()
-Header 'Manage Icinga for Windows:' `
-Entries @(
@{
'Caption' = 'Manage background daemons';
'Command' = 'Show-IcingaForWindowsManagementConsoleManageBackgroundDaemons';
'Help' = 'Allows you to manage Icinga for Windows background daemons';
'Disabled' = ($null -eq (Get-Service 'icingapowershell' -ErrorAction SilentlyContinue));
'Caption' = 'Manage background daemons';
'Command' = 'Show-IcingaForWindowsManagementConsoleManageBackgroundDaemons';
'Help' = 'Allows you to manage Icinga for Windows background daemons';
'Disabled' = ($null -eq (Get-Service 'icingapowershell' -ErrorAction SilentlyContinue));
'DisabledReason' = 'Icinga for Windows service is not installed';
},
@{
'Caption' = 'Manage Icinga Repositories';
'Command' = 'Show-IcingaForWindowsManagementConsoleManageIcingaRepositories';
'Help' = 'Allows you to manage Icinga for Windows repositories';
},
@{
'Caption' = 'Manage JEA profile';
'Command' = 'Show-IcingaForWindowsManagementConsoleManageJEA';
'Help' = 'Allows you to manage Icinga for Windows JEA profile';
'Disabled' = $JEADisabled;
'DisabledReason' = ([string]::Format('PowerShell version "{0}" is lower than 5.0 or you are not inside an administrative shell', $PSVersionTable.PSVersion.ToString(2)));
},
@{
'Caption' = ([string]::Format('Framework Debug Mode: {0}', (& { if ($FrameworkDebug) { 'Enabled' } else { 'Disabled' } } )));
'Command' = 'Show-IcingaForWindowsManagementConsoleManageFramework';
'Help' = 'Disable or enable the Icinga PowerShell Framework debug mode';
'Disabled' = $FALSE;
'Action' = @{
'Command' = 'Invoke-IcingaForWindowsMangementConsoleToogleFrameworkDebug';
'Arguments' = @{ };
'Command' = 'Invoke-IcingaForWindowsMangementConsoleToogleFrameworkDebug';
}
},
@{
@ -38,8 +50,7 @@ function Show-IcingaForWindowsManagementConsoleManageFramework()
'Command' = 'Show-IcingaForWindowsManagementConsoleManageFramework';
'Help' = 'Updates the Icinga PowerShell Framework Code Cache';
'Action' = @{
'Command' = 'Write-IcingaFrameworkCodeCache';
'Arguments' = @{ };
'Command' = 'Write-IcingaFrameworkCodeCache';
}
},
@{
@ -48,8 +59,7 @@ function Show-IcingaForWindowsManagementConsoleManageFramework()
'Help' = 'Enables the Icinga untrusted certificate validation, allowing you to communicate with web servers which ships with a self-signed certificate not installed on this system. This applies only to this PowerShell session and is not permanent. Might be helpful in case you want to connect to the Icinga Director and the SSL is not trusted by this host';
'Disabled' = $FALSE
'Action' = @{
'Command' = 'Enable-IcingaUntrustedCertificateValidation';
'Arguments' = @{ };
'Command' = 'Enable-IcingaUntrustedCertificateValidation';
}
},
@{
@ -59,31 +69,34 @@ function Show-IcingaForWindowsManagementConsoleManageFramework()
'Disabled' = $FALSE
},
@{
'Caption' = 'Start Icinga for Windows Service';
'Command' = 'Show-IcingaForWindowsManagementConsoleManageFramework';
'Help' = 'Allows you to start the Icinga for Windows Service if the service is not running';
'Disabled' = ($null -eq $IcingaService -Or $ServiceStatus -eq 'Running' -Or (-Not $AdminShell));
'Action' = @{
'Caption' = 'Start Icinga for Windows Service';
'Command' = 'Show-IcingaForWindowsManagementConsoleManageFramework';
'Help' = 'Allows you to start the Icinga for Windows Service if the service is not running';
'Disabled' = ($null -eq $IcingaService -Or $ServiceStatus -eq 'Running' -Or (-Not $AdminShell));
'DisabledReason' = 'The service is either not installed, already running or you are not inside an administrative shell';
'Action' = @{
'Command' = 'Start-Service';
'Arguments' = @{ '-Name' = 'icingapowershell'; };
}
},
@{
'Caption' = 'Stop Icinga for Windows Service';
'Command' = 'Show-IcingaForWindowsManagementConsoleManageFramework';
'Help' = 'Allows you to stop the Icinga for Windows Service if the service is not running';
'Disabled' = ($null -eq $IcingaService -Or $ServiceStatus -ne 'Running' -Or (-Not $AdminShell));
'Action' = @{
'Caption' = 'Stop Icinga for Windows Service';
'Command' = 'Show-IcingaForWindowsManagementConsoleManageFramework';
'Help' = 'Allows you to stop the Icinga for Windows Service if the service is not running';
'Disabled' = ($null -eq $IcingaService -Or $ServiceStatus -ne 'Running' -Or (-Not $AdminShell));
'DisabledReason' = 'The service is either not installed, already stopped or you are not inside an administrative shell';
'Action' = @{
'Command' = 'Stop-Service';
'Arguments' = @{ '-Name' = 'icingapowershell'; };
}
},
@{
'Caption' = 'Restart Icinga for Windows Service';
'Command' = 'Show-IcingaForWindowsManagementConsoleManageFramework';
'Help' = 'Allows you to restart the Icinga for Windows Service if the service is installed';
'Disabled' = ($null -eq $IcingaService -Or (-Not $AdminShell));
'Action' = @{
'Caption' = 'Restart Icinga for Windows Service';
'Command' = 'Show-IcingaForWindowsManagementConsoleManageFramework';
'Help' = 'Allows you to restart the Icinga for Windows Service if the service is installed';
'Disabled' = ($null -eq $IcingaService -Or (-Not $AdminShell));
'DisabledReason' = 'The service is either not installed or you are not inside an administrative shell';
'Action' = @{
'Command' = 'Restart-Service';
'Arguments' = @{ '-Name' = 'icingapowershell'; };
}

View file

@ -0,0 +1,100 @@
function Show-IcingaForWindowsManagementConsoleManageJEA()
{
Show-IcingaForWindowsInstallerMenu `
-Header 'Manage Icinga for Windows JEA configuration:' `
-Entries @(
@{
'Caption' = 'Install JEA profile with managed user "icinga"';
'Command' = 'Show-IcingaForWindowsManagementConsoleManageJEA';
'Help' = 'Will create a managed user called "icinga", updates the services "icinga2" and "icingapowershell" with the new user and creates a JEA profile based on installed modules';
'Action' = @{
'Command' = 'Show-IcingaWindowsManagementConsoleYesNoDialog';
'Arguments' = @{
'-Caption' = 'Install JEA profile with managed user "icinga"';
'-Command' = 'Install-IcingaSecurity';
}
}
},
@{
'Caption' = 'Install/Update JEA profile';
'Command' = 'Show-IcingaForWindowsManagementConsoleManageJEA';
'Help' = 'Installs or updates the JEA profile for Icinga for Windows for the current user assigned to the "icinga2" service';
'Action' = @{
'Command' = 'Show-IcingaWindowsManagementConsoleYesNoDialog';
'Arguments' = @{
'-Caption' = 'Install/Update JEA profile';
'-Command' = 'Install-IcingaJEAProfile';
}
}
},
@{
'Caption' = 'Install JEA profile with "ConstrainedLanguage" and managed user "icinga"';
'Command' = 'Show-IcingaForWindowsManagementConsoleManageJEA';
'Help' = 'Will create a managed user called "icinga", updates the services "icinga2" and "icingapowershell" with the new user and creates a JEA profile with "ConstrainedLanguage" based on installed modules. This is NOT recommended in case you are using the Icinga for Windows service, as it will not work in "ConstrainedLanguage" mode';
'Action' = @{
'Command' = 'Show-IcingaWindowsManagementConsoleYesNoDialog';
'Arguments' = @{
'-Caption' = 'Install JEA profile with "ConstrainedLanguage" and managed user "icinga"';
'-Command' = 'Install-IcingaSecurity';
'-CmdArguments' = @{
'-ConstrainedLanguage' = $TRUE;
}
}
}
},
@{
'Caption' = 'Install/Update JEA profile with "ConstrainedLanguage"';
'Command' = 'Show-IcingaForWindowsManagementConsoleManageJEA';
'Help' = 'Installs or updates the JEA profile for Icinga for Windows for the current user assigned to the "icinga2" service with "ConstrainedLanguage". This is NOT recommended in case you are using the Icinga for Windows service, as it will not work in "ConstrainedLanguage" mode';
'Action' = @{
'Command' = 'Show-IcingaWindowsManagementConsoleYesNoDialog';
'Arguments' = @{
'-Caption' = 'Install/Update JEA profile with "ConstrainedLanguage"';
'-Command' = 'Install-IcingaJEAProfile';
'-CmdArguments' = @{
'-ConstrainedLanguage' = $TRUE;
}
}
}
},
@{
'Caption' = 'Uninstall JEA profile and managed user "icinga"';
'Command' = 'Show-IcingaForWindowsManagementConsoleManageJEA';
'Help' = 'This will uninstall the JEA profile and remove the managed user "icinga" from the system. For the services the default user "NT Authority\NetworkService" will be applied';
'Action' = @{
'Command' = 'Show-IcingaWindowsManagementConsoleYesNoDialog';
'Arguments' = @{
'-Caption' = 'Uninstall JEA profile and managed user "icinga"';
'-Command' = 'Uninstall-IcingaSecurity';
}
}
},
@{
'Caption' = 'Uninstall JEA profile';
'Command' = 'Show-IcingaForWindowsManagementConsoleManageJEA';
'Help' = 'Uninstalls the JEA profile from this system';
'Action' = @{
'Command' = 'Show-IcingaWindowsManagementConsoleYesNoDialog';
'Arguments' = @{
'-Caption' = 'Uninstall JEA profile';
'-Command' = 'Uninstall-IcingaJEAProfile';
}
}
},
@{
'Caption' = 'Rebuild Icinga PowerShell Framework dependency cache';
'Command' = 'Show-IcingaForWindowsManagementConsoleManageJEA';
'Help' = 'Rebuilds the Icinga for Windows Framework dependency cache for JEA profile generation';
'Action' = @{
'Command' = 'Show-IcingaWindowsManagementConsoleYesNoDialog';
'Arguments' = @{
'-Caption' = 'Rebuild Icinga PowerShell Framework dependency cache';
'-Command' = 'Get-IcingaJEAConfiguration';
'-CmdArguments' = @{
'-RebuildFramework' = $TRUE;
}
}
}
}
);
}

View file

@ -0,0 +1,18 @@
function Add-IcingaForWindowsInstallerDisabledEntry()
{
param (
[string]$Name = '',
[string]$Reason = ''
);
if ([string]::IsNullOrEmpty($Reason)) {
$Reason = 'Generic disable message';
}
if ($Global:Icinga.InstallWizard.DisabledEntries.ContainsKey($Name)) {
$Global:Icinga.InstallWizard.DisabledEntries[$Name] = $Reason;
return;
}
$Global:Icinga.InstallWizard.DisabledEntries.Add($Name, $Reason);
}

View file

@ -0,0 +1,12 @@
function Get-IcingaForWindowsInstallerDisabledEntry()
{
param (
[string]$Name = ''
);
if ($Global:Icinga.InstallWizard.DisabledEntries.ContainsKey($Name)) {
return ($Global:Icinga.InstallWizard.DisabledEntries[$Name]);
}
return '';
}

View file

@ -376,11 +376,12 @@ function Show-IcingaForWindowsInstallerMenu()
};
}
$DisabledMenu = $FALSE;
$NextMenu = $null;
$NextArguments = @{ };
$ActionCmd = $null;
$ActionArgs = $null;
[bool]$DisabledMenu = $FALSE;
[string]$DisabledReason = '';
$NextMenu = $null;
$NextArguments = @{ };
$ActionCmd = $null;
$ActionArgs = $null;
if ([string]::IsNullOrEmpty($Result) -eq $FALSE) {
if ($Result -eq 'c') {
@ -390,6 +391,9 @@ function Show-IcingaForWindowsInstallerMenu()
$NextMenu = $Entries[0].Command;
if ($null -ne $Entries[0].Disabled) {
$DisabledMenu = $Entries[0].Disabled;
if ($null -ne $Entries[0].DisabledReason) {
$DisabledReason = $Entries[0].DisabledReason;
}
}
}
$ActionCmd = $Entries[0].Action.Command;
@ -398,6 +402,9 @@ function Show-IcingaForWindowsInstallerMenu()
$NextMenu = $Entries[$Result].Command;
if ($null -ne $Entries[$Result].Disabled) {
$DisabledMenu = $Entries[$Result].Disabled;
if ($null -ne $Entries[0].DisabledReason) {
$DisabledReason = $Entries[0].DisabledReason;
}
}
if ($Entries[$Result].ContainsKey('Arguments')) {
$NextArguments = $Entries[$Result].Arguments;
@ -408,7 +415,10 @@ function Show-IcingaForWindowsInstallerMenu()
}
if ($DisabledMenu) {
$global:Icinga.InstallWizard.LastNotice = [string]::Format('This menu is not enabled: {0}', $Result);
if ([string]::IsNullOrEmpty($DisabledReason) -eq $FALSE) {
$DisabledReason = [string]::Format(' => Reason: {0}', $DisabledReason);
}
$global:Icinga.InstallWizard.LastNotice = [string]::Format('This menu is not enabled: {0}{1}', $Result, $DisabledReason);
return;
}

View file

@ -12,7 +12,7 @@ function Write-IcingaManagementConsoleCommand()
if ($Entry.Action -And ($Entry.Action.ContainsKey('Command') -Or ($Entry.Action.ContainsKey('Arguments') -And $Entry.Action.Arguments.ContainsKey('-Command')))) {
$PrintArguments = '';
$PrintCommand = ''
if ($Entry.Action.Arguments.ContainsKey('-CmdArguments')) {
if ($null -ne $Entry.Action.Arguments -And $Entry.Action.Arguments.ContainsKey('-CmdArguments')) {
$PrintCommand = $Entry.Action.Arguments['-Command'];
foreach ($cmdArg in $Entry.Action.Arguments['-CmdArguments'].Keys) {
$PrintValue = $Entry.Action.Arguments['-CmdArguments'][$cmdArg];
@ -34,23 +34,25 @@ function Write-IcingaManagementConsoleCommand()
}
} else {
$PrintCommand = $Entry.Action.Command;
foreach ($cmdArg in $Entry.Action.Arguments.Keys) {
$PrintValue = $Entry.Action.Arguments[$cmdArg];
[string]$StringArg = ([string]$cmdArg).Replace('-', '');
if ($PrintValue.GetType().Name -eq 'Boolean') {
if ((Get-Command $PrintCommand).Parameters.$StringArg.ParameterType.Name -eq 'SwitchParameter') {
$PrintValue = '';
} else {
if ($PrintValue) {
$PrintValue = '$TRUE';
if ($null -ne $Entry.Action.Arguments) {
foreach ($cmdArg in $Entry.Action.Arguments.Keys) {
$PrintValue = $Entry.Action.Arguments[$cmdArg];
[string]$StringArg = ([string]$cmdArg).Replace('-', '');
if ($PrintValue.GetType().Name -eq 'Boolean') {
if ((Get-Command $PrintCommand).Parameters.$StringArg.ParameterType.Name -eq 'SwitchParameter') {
$PrintValue = '';
} else {
$PrintValue = '$FALSE';
if ($PrintValue) {
$PrintValue = '$TRUE';
} else {
$PrintValue = '$FALSE';
}
}
} elseif ($PrintValue.GetType().Name -eq 'String' -And $PrintValue.Contains(' ')) {
$PrintValue = (ConvertFrom-IcingaArrayToString -Array $PrintValue -AddQuotes);
}
} elseif ($PrintValue.GetType().Name -eq 'String' -And $PrintValue.Contains(' ')) {
$PrintValue = (ConvertFrom-IcingaArrayToString -Array $PrintValue -AddQuotes);
$PrintArguments += ([string]::Format('{0} {1} ', $cmdArg, $PrintValue));
}
$PrintArguments += ([string]::Format('{0} {1} ', $cmdArg, $PrintValue));
}
}
@ -60,7 +62,11 @@ function Write-IcingaManagementConsoleCommand()
$PrintArguments = $PrintArguments.SubString(0, $PrintArguments.Length - 1);
}
Write-IcingaConsolePlain ([string]::Format('PS> {0} {1};', $PrintCommand, $PrintArguments)) -ForeColor Magenta;
if ([string]::IsNullOrEmpty($PrintArguments) -eq $FALSE) {
$PrintArguments = [string]::Format(' {0}', $PrintArguments);
}
Write-IcingaConsolePlain ([string]::Format('PS> {0}{1};', $PrintCommand, $PrintArguments)) -ForeColor Magenta;
Write-IcingaConsolePlain '';
}
}

View file

@ -0,0 +1,51 @@
function Get-IcingaCommandDependency()
{
param (
$DependencyList = (New-Object PSCustomObject),
[hashtable]$CompiledList = @{ },
[string]$CmdName = '',
[string]$CmdType = ''
);
if ([string]::IsNullOrEmpty($CmdType)) {
return $CompiledList;
}
if ($CompiledList.ContainsKey($CmdType) -eq $FALSE) {
$CompiledList.Add($CmdType, @{ });
}
if ($CompiledList[$CmdType].ContainsKey($CmdName)) {
$CompiledList[$CmdType][$CmdName] += 1;
return $CompiledList;
}
$CompiledList[$CmdType].Add($CmdName, 0);
if ((Test-PSCustomObjectMember -PSObject $DependencyList -Name $CmdName) -eq $FALSE) {
return $CompiledList;
}
foreach ($CmdList in $DependencyList.$CmdName.PSObject.Properties.Name) {
$Cmd = $DependencyList.$CmdName.$CmdList;
if ($CompiledList.ContainsKey($CmdList) -eq $FALSE) {
$CompiledList.Add($CmdList, @{ });
}
foreach ($entry in $Cmd.PSObject.Properties.Name) {
if ($CompiledList[$CmdList].ContainsKey($entry) -eq $FALSE) {
$CompiledList[$CmdList].Add($entry, 0);
$CompiledList = Get-IcingaCommandDependency `
-DependencyList $DependencyList `
-CompiledList $CompiledList `
-CmdName $entry;
} else {
$CompiledList[$CmdList][$entry] += 1;
}
}
}
return $CompiledList;
}

View file

@ -0,0 +1,44 @@
function Get-IcingaFrameworkDependency()
{
param (
[string]$Command = $null,
$DependencyList = (New-Object PSCustomObject)
);
if (Test-PSCustomObjectMember -PSObject $DependencyList -Name $Command) {
return $DependencyList;
}
$DependencyList | Add-Member -MemberType NoteProperty -Name ($Command) -Value (New-Object PSCustomObject);
$CommandConfig = (Get-Command $Command);
$ModuleContent = $CommandConfig.ScriptBlock.ToString();
$DeserializedFile = Read-IcingaPowerShellModuleFile -FileContent $ModuleContent;
[array]$CheckCmd = $DeserializedFile.CommandList + $DeserializedFile.FunctionList;
foreach ($cmd in $CheckCmd) {
if ($cmd -eq $Command) {
continue;
}
$CommandConfig = Get-Command $cmd -ErrorAction SilentlyContinue;
if ($null -eq $CommandConfig) {
continue;
}
[string]$CommandType = ([string]$CommandConfig.CommandType).Replace(' ', '');
if ((Test-PSCustomObjectMember -PSObject ($DependencyList.$Command) -Name $CommandType) -eq $FALSE) {
$DependencyList.$Command | Add-Member -MemberType NoteProperty -Name ($CommandType) -Value (New-Object PSCustomObject);
}
if ((Test-PSCustomObjectMember -PSObject ($DependencyList.$Command.$CommandType) -Name $cmd) -eq $FALSE) {
$DependencyList.$Command.$CommandType | Add-Member -MemberType NoteProperty -Name ($cmd) -Value 0;
}
$DependencyList.$Command.$CommandType.($cmd) += 1;
}
return $DependencyList;
}

View file

@ -0,0 +1,169 @@
function Get-IcingaJEAConfiguration()
{
param (
[switch]$RebuildFramework = $FALSE,
[switch]$AllowScriptBlocks = $FALSE
);
# Prepare all variables and content we require for building the profile
$CommandList = Get-Command;
$PowerShellModules = Get-ChildItem -Path (Get-IcingaForWindowsRootPath) -Filter 'icinga-powershell-*';
[array]$BlockedModules = @();
$DependencyList = New-Object PSCustomObject;
[hashtable]$UsedCmdlets = @{
'Alias' = @{ };
'Cmdlet' = @{ };
'Function' = @{ };
'Modules' = ([System.Collections.ArrayList]@());
};
$ModuleContent = '';
[bool]$DependencyCache = $FALSE;
if ((Test-Path (Join-Path -Path (Get-IcingaCacheDir) -ChildPath 'framework_dependencies.json')) -And $RebuildFramework -eq $FALSE) {
$DependencyList = ConvertFrom-Json -InputObject (Read-IcingaFileSecure -File (Join-Path -Path (Get-IcingaCacheDir) -ChildPath 'framework_dependencies.json'));
$DependencyCache = $TRUE;
}
# Lookup all PowerShell modules installed for Icinga for Windows inside the same folder as the Framework
# and fetch each single module file to list the used Cmdlets and Functions
# Add each file content to a big string file for better parsing
foreach ($module in $PowerShellModules) {
$Progress = Write-IcingaProgressStatus -Message 'Fetching Icinga for Windows Components' -CurrentValue $Progress -MaxValue $PowerShellModules.Count -Details;
if ($module.Name.ToLower() -eq 'icinga-powershell-framework') {
continue;
}
if ($UsedCmdlets.Modules -NotContains $module.Name) {
$UsedCmdlets.Modules.Add($module.Name) | Out-Null;
}
$ModuleFiles = Get-ChildItem -Path $module.FullName -Recurse -Include '*.psm1';
$ModuleFileContent = '';
foreach ($PSFile in $ModuleFiles) {
$DeserializedFile = Read-IcingaPowerShellModuleFile -File $PSFile.FullName;
$RawModuleContent = $DeserializedFile.NormalisedContent;
if ([string]::IsNullOrEmpty($RawModuleContent)) {
continue;
}
$ModuleFileContent += $RawModuleContent;
$ModuleFileContent += "`r`n";
$ModuleFileContent += "`r`n";
$SourceCode = $RawModuleContent.ToLower().Replace(' ', '');
$SourceCode = $SourceCode.Replace("`r`n", '');
$SourceCode = $SourceCode.Replace("`n", '');
# Lookup the entire command list and compare the source code behind if it contains any [ScriptBlocks] or Add-Types
# [ScriptBlocks] are forbidden and modules containing them will not be added, while Add-Type will print a warning
if ($null -ne (Select-String -InputObject $ModuleFileContent -Pattern '[scriptblock]' -SimpleMatch) -Or $null -ne (Select-String -InputObject $SourceCode -Pattern '={' -SimpleMatch) -Or $null -ne (Select-String -InputObject $SourceCode -Pattern 'return{' -SimpleMatch) -Or $null -ne (Select-String -InputObject $SourceCode -Pattern ';{' -SimpleMatch)) {
if ($AllowScriptBlocks -eq $FALSE) {
Write-IcingaConsoleError 'Unable to include module "{0}" into JEA profile. The file "{1}" is using one or more [ScriptBlock] variables which are forbidden in JEA context.' -Objects $module.Name, $PSFile.FullName;
$UsedCmdlets.Modules.RemoveAt($UsedCmdlets.Modules.IndexOf($module.Name));
$BlockedModules += $module.Name;
$ModuleFileContent = '';
break;
} else {
Write-IcingaConsoleWarning 'Module "{0}" is containing [ScriptBlock] like content inside file "{1}". Please validate the file before running it inside JEA context.' -Objects $module.Name, $PSFile.FullName;
}
}
if ($null -ne (Select-String -InputObject $SourceCode -Pattern 'add-type' -SimpleMatch) -Or $null -ne (Select-String -InputObject $SourceCode -Pattern 'typedefinition@"' -SimpleMatch) -Or $null -ne (Select-String -InputObject $SourceCode -Pattern '@"' -SimpleMatch)) {
Write-IcingaConsoleWarning 'The module "{0}" is using "Add-Type" definitions for file "{1}". Ensure you validate the code before trusting this publisher.' -Objects $module.Name, $PSFile.FullName;
}
}
$ModuleContent += $ModuleFileContent;
}
if ($DependencyCache -eq $FALSE) {
# Now lets lookup every single Framework file and get all used Cmdlets and Functions so we know our dependencies
$FrameworkFiles = Get-ChildItem -Path (Get-IcingaFrameworkRootPath) -Recurse -Filter '*.psm1';
foreach ($ModuleFile in $FrameworkFiles) {
$Progress = Write-IcingaProgressStatus -Message 'Compiling Icinga PowerShell Framework Dependency List' -CurrentValue $Progress -MaxValue $FrameworkFiles.Count -Details;
# Just ignore our cache file
if ($ModuleFile.FullName -eq (Get-IcingaFrameworkCodeCacheFile)) {
continue;
}
$DeserializedFile = Read-IcingaPowerShellModuleFile -File $ModuleFile.FullName;
foreach ($FoundFunction in $DeserializedFile.FunctionList) {
$DependencyList = Get-IcingaFrameworkDependency `
-Command $FoundFunction `
-DependencyList $DependencyList;
}
}
Write-IcingaFileSecure -File (Join-Path -Path (Get-IcingaCacheDir) -ChildPath 'framework_dependencies.json') -Value $DependencyList;
}
$UsedCmdlets.Modules.Add('icinga-powershell-framework') | Out-Null;
# Check all our configured background daemons and ensure we get all Cmdlets and Functions including the dependency list
$BackgroundDaemons = (Get-IcingaBackgroundDaemons).Keys;
foreach ($daemon in $BackgroundDaemons) {
$Progress = Write-IcingaProgressStatus -Message 'Compiling Background Daemon Dependency List' -CurrentValue $Progress -MaxValue $BackgroundDaemons.Count -Details;
$DaemonCmd = (Get-Command $daemon);
if ($BlockedModules -Contains $DaemonCmd.Source) {
continue;
}
$ModuleContent += [string]::Format('function {0} {{{1}{2}{1}}}', $daemon, "`r`n", $DaemonCmd.ScriptBlock.ToString());
[string]$CommandType = ([string]$DaemonCmd.CommandType).Replace(' ', '');
$UsedCmdlets = Get-IcingaCommandDependency `
-DependencyList $DependencyList `
-CompiledList $UsedCmdlets `
-CmdName $DaemonCmd.Name `
-CmdType $CommandType;
}
# We need to add this function which is not used anywhere else and should still add the entire dependency tree
$UsedCmdlets = Get-IcingaCommandDependency `
-DependencyList $DependencyList `
-CompiledList $UsedCmdlets `
-CmdName 'Exit-IcingaExecutePlugin' `
-CmdType 'Function';
# We need to add this function for our background daemon we start with 'Start-IcingaPowerShellDaemon',
# as this function is called outside the JEA context
$UsedCmdlets = Get-IcingaCommandDependency `
-DependencyList $DependencyList `
-CompiledList $UsedCmdlets `
-CmdName 'Add-IcingaForWindowsDaemon' `
-CmdType 'Function';
# Finally loop through all commands again and build our JEA command list
$DeserializedFile = Read-IcingaPowerShellModuleFile -FileContent $ModuleContent;
[array]$JeaCmds = $DeserializedFile.CommandList + $DeserializedFile.FunctionList;
foreach ($cmd in $JeaCmds) {
$Progress = Write-IcingaProgressStatus -Message 'Compiling JEA Profile Catalog' -CurrentValue $Progress -MaxValue $JeaCmds.Count -Details;
$CmdData = Get-Command $cmd -ErrorAction SilentlyContinue;
if ($null -eq $CmdData) {
continue;
}
$CommandType = ([string]$CmdData.CommandType).Replace(' ', '');
$UsedCmdlets = Get-IcingaCommandDependency `
-DependencyList $DependencyList `
-CompiledList $UsedCmdlets `
-CmdName $cmd `
-CmdType $CommandType;
}
Disable-IcingaProgressPreference;
return $UsedCmdlets;
}

View file

@ -0,0 +1,4 @@
function Get-IcingaJEAContext()
{
return (Get-IcingaPowerShellConfig -Path 'Framework.JEAProfile');
}

View file

@ -0,0 +1,11 @@
function Get-IcingaJEAServicePid()
{
[string]$JeaPidFile = (Join-Path -Path (Get-IcingaCacheDir) -ChildPath 'jea.pid');
[string]$JeaPid = Read-IcingaFileSecure -File $JeaPidFile;
if ([string]::IsNullOrEmpty($JeaPid) -eq $FALSE) {
$JeaPid = $JeaPid.Replace("`r`n", '').Replace("`n", '').Replace(' ', '');
}
return $JeaPid;
}

View file

@ -0,0 +1,10 @@
function Get-IcingaJEASessionFile()
{
[string]$Path = Join-Path -Path (Get-IcingaFrameworkRootPath) -ChildPath 'RoleCapabilities\IcingaForWindows.psrc';
if (Test-Path -Path $Path) {
return $Path;
}
return '';
}

View file

@ -0,0 +1,22 @@
function Install-IcingaJEAProfile()
{
param (
[string]$IcingaUser = ((Get-IcingaServices).icinga2.configuration.ServiceUser),
[switch]$ConstrainedLanguage = $FALSE,
[switch]$TestEnv = $FALSE,
[switch]$RebuildFramework = $FALSE,
[switch]$AllowScriptBlocks = $FALSE
);
if ($PSVersionTable.PSVersion -lt '5.0.0.0') {
Write-IcingaConsoleError 'You cannot use JEA profiles on your system, as your installed PowerShell version "{0}" is lower than minimum required version "5.0"' -Objects $PSVersionTable.PSVersion;
return;
}
Write-IcingaConsoleNotice 'Writing Icinga for Windows environment information as JEA profile'
Write-IcingaJEAProfile -RebuildFramework:$RebuildFramework -AllowScriptBlocks:$AllowScriptBlocks;
Write-IcingaConsoleNotice 'Registering Icinga for Windows JEA profile'
Register-IcingaJEAProfile -IcingaUser $IcingaUser -TestEnv:$TestEnv -ConstrainedLanguage:$ConstrainedLanguage;
}
Set-Alias -Name 'Update-IcingaJEAProfile' -Value 'Install-IcingaJEAProfile';

View file

@ -0,0 +1,58 @@
function Read-IcingaPowerShellModuleFile()
{
param (
[string]$File,
[string]$FileContent = ''
);
if (([string]::IsNullOrEmpty($File) -Or (Test-Path -Path $File) -eq $FALSE) -And [string]::IsNullOrEmpty($FileContent)) {
return '';
}
if ([string]::IsNullOrEmpty($FileContent)) {
$FileContent = Read-IcingaFileSecure -File $File;
}
$PSParser = [System.Management.Automation.PSParser]::Tokenize($FileContent, [ref]$null);
[array]$Comments = @();
[array]$RegexFilter = @();
[string]$RegexPattern = '';
[array]$CommandList = @();
[array]$FunctionList = @();
[hashtable]$CmdCache = @{ };
[hashtable]$FncCache = @{ };
[int]$Index = 0;
foreach ($entry in $PSParser) {
if ($entry.Type -eq 'Comment') {
$Comments += Select-Object -InputObject $entry -ExpandProperty 'Content';
} elseif ($entry.Type -eq 'Command') {
if ($CmdCache.ContainsKey($entry.Content) -eq $FALSE) {
$CommandList += [string]$entry.Content;
$CmdCache.Add($entry.Content, 0);
}
} elseif ($entry.Type -eq 'CommandArgument') {
if ($PSParser[$index - 1].Type -eq 'Keyword' -And $PSParser[$index - 1].Content.ToLower() -eq 'function') {
if ($FncCache.ContainsKey($entry.Content) -eq $FALSE) {
$FunctionList += [string]$entry.Content;
$FncCache.Add($entry.Content, 0);
}
}
}
$Index += 1;
}
foreach ($entry in $Comments) {
$RegexFilter += [regex]::Escape($entry);
}
$RegexPattern = [string]::Join('|', $RegexFilter);
return @{
'NormalisedContent' = ($FileContent -Replace $RegexPattern -Split '\r?\n' -NotMatch '^\s*$');
'RawContent' = $FileContent;
'CommandList' = $CommandList;
'FunctionList' = $FunctionList;
};
}

View file

@ -0,0 +1,49 @@
function Register-IcingaJEAProfile()
{
param (
[string]$IcingaUser = ((Get-IcingaServices).icinga2.configuration.ServiceUser),
[switch]$ConstrainedLanguage = $FALSE,
[switch]$TestEnv = $FALSE
);
$JeaTemplate = Join-Path -Path (Get-IcingaFrameworkRootPath) -ChildPath 'templates\IcingaForWindows.pssc.template';
$JeaProfile = Join-Path -Path (Get-IcingaFrameworkRootPath) -ChildPath 'IcingaForWindows.pssc';
$JeaContent = Get-Content -Path $JeaTemplate -Raw;
$JeaName = 'IcingaForWindows';
if ($TestEnv) {
$IcingaUser = $ENV:USERNAME;
$JeaProfile = Join-Path -Path (Get-IcingaFrameworkRootPath) -ChildPath 'IcingaForWindowsTest.pssc';
$JeaName = 'IcingaForWindowsTest';
}
if ([string]::IsNullOrEmpty($IcingaUser)) {
Write-IcingaConsoleError 'No user found to set the JEA profile to. By default the Icinga Agent user is used for this';
return;
}
$LanguageMode = 'FullLanguage';
if ($ConstrainedLanguage) {
$LanguageMode = 'ConstrainedLanguage';
}
$UserSID = Get-IcingaUserSID -User $IcingaUser;
$IcingaUser = Get-IcingaUsernameFromSID -SID $UserSID;
$JeaContent = $JeaContent.Replace('$ICINGAFORWINDOWSJEAUSER$', $IcingaUser);
$JeaContent = $JeaContent.Replace('$POWERSHELLLANGUAGEMODE$', $LanguageMode);
Set-Content -Path $JeaProfile -Value $JeaContent;
$Result = Register-PSSessionConfiguration -Name $JeaName -Path $JeaProfile -Force;
if ($TestEnv -eq $FALSE) {
Set-IcingaPowerShellConfig -Path 'Framework.JEAProfile' -Value 'IcingaForWindows';
}
if ($null -ne $Result) {
Write-IcingaConsoleNotice 'JEA Profile "{0}" was successfully installed' -Objects $Result.Name;
} else {
Write-IcingaConsoleNotice 'Failed to install JEA profile';
}
}

View file

@ -0,0 +1,10 @@
function Remove-IcingaFrameworkDependencyFile()
{
$DependencyFile = Join-Path -Path (Get-IcingaCacheDir) -ChildPath 'framework_dependencies.json';
if (-Not (Test-Path $DependencyFile)) {
return;
}
Remove-ItemSecure -Path $DependencyFile -Force | Out-Null;
}

View file

@ -0,0 +1,21 @@
function Test-IcingaJEAServiceRunning()
{
param (
[string]$JeaPid = $null
);
if ([string]::IsNullOrEmpty($JeaPid)) {
[string]$JeaPid = Get-IcingaJEAServicePid;
}
$JeaPowerShellProcess = Get-Process -Id $JeaPid -ErrorAction SilentlyContinue;
if ($null -eq $JeaPowerShellProcess) {
return $FALSE;
}
if ($JeaPowerShellProcess.ProcessName -ne 'wsmprovhost') {
return $FALSE;
}
return $TRUE;
}

View file

@ -0,0 +1,59 @@
function Test-IcingaPowerShellCommandInCode()
{
param (
[string]$Code = '',
[string]$Command = ''
);
if ([string]::IsNullOrEmpty($Code) -Or [string]::IsNullOrEmpty($Command)) {
return $FALSE;
}
[string]$SearchCmdSpace = [string]::Format('{0} ', $Command);
[string]$SearchCmdColon = [string]::Format('{0};', $Command);
[string]$SearchCmdCBClose = [string]::Format('{0})', $Command);
[string]$SearchCmdCBOpen = [string]::Format('{0}(', $Command);
[string]$SearchCmdSB = [string]::Format('{0}]', $Command);
[string]$SearchCmdBrace = [string]::Format('{0}}}', $Command);
[string]$SearchCmdSQ = [string]::Format("{0}'", $Command);
[string]$SearchCmdRN = [string]::Format('{0}{1}', $Command, "`r`n");
[string]$SearchCmdNL = [string]::Format('{0}{1}', $Command, "`n");
if ($null -ne (Select-String -InputObject $ModuleContent -Pattern $SearchCmdSpace -SimpleMatch)) {
return $TRUE;
}
if ($null -ne (Select-String -InputObject $ModuleContent -Pattern $SearchCmdColon -SimpleMatch)) {
return $TRUE;
}
if ($null -ne (Select-String -InputObject $ModuleContent -Pattern $SearchCmdCBOpen -SimpleMatch)) {
return $TRUE;
}
if ($null -ne (Select-String -InputObject $ModuleContent -Pattern $SearchCmdCBClose -SimpleMatch)) {
return $TRUE;
}
if ($null -ne (Select-String -InputObject $ModuleContent -Pattern $SearchCmdSB -SimpleMatch)) {
return $TRUE;
}
if ($null -ne (Select-String -InputObject $ModuleContent -Pattern $SearchCmdBrace -SimpleMatch)) {
return $TRUE;
}
if ($null -ne (Select-String -InputObject $ModuleContent -Pattern $SearchCmdSQ -SimpleMatch)) {
return $TRUE;
}
if ($null -ne (Select-String -InputObject $ModuleContent -Pattern $SearchCmdRN -SimpleMatch)) {
return $TRUE;
}
if ($null -ne (Select-String -InputObject $ModuleContent -Pattern $SearchCmdNL -SimpleMatch)) {
return $TRUE;
}
return $FALSE;
}

View file

@ -0,0 +1,20 @@
function Uninstall-IcingaJEAProfile()
{
$JeaProfile = Join-Path -Path (Get-IcingaFrameworkRootPath) -ChildPath 'IcingaForWindows.pssc';
$JeaProfileRessource = Join-Path -Path (Get-IcingaFrameworkRootPath) -ChildPath 'RoleCapabilities\IcingaForWindows.psrc';
if (Test-Path $JeaProfile) {
Write-IcingaConsoleNotice 'Removing JEA profile';
Remove-Item $JeaProfile -Force;
}
if (Test-Path $JeaProfileRessource) {
Write-IcingaConsoleNotice 'Removing JEA profile ressource';
Remove-Item $JeaProfileRessource -Force;
}
Write-IcingaConsoleNotice 'Removing JEA profile registration';
Unregister-PSSessionConfiguration -Name 'IcingaForWindows' -Force -ErrorAction SilentlyContinue;
Set-IcingaPowerShellConfig -Path 'Framework.JEAProfile' -Value '';
}

View file

@ -0,0 +1,30 @@
function Write-IcingaJEAProfile()
{
param (
[switch]$RebuildFramework = $FALSE,
[switch]$AllowScriptBlocks = $FALSE
);
[hashtable]$JeaConfig = Get-IcingaJEAConfiguration -RebuildFramework:$RebuildFramework -AllowScriptBlocks:$AllowScriptBlocks;
$JeaFile = Join-Path -Path (Get-IcingaFrameworkRootPath) -ChildPath 'templates\IcingaForWindows.psrc.template';
$JeaString = Get-Content $JeaFile;
$NewJeaFile = '';
foreach ($line in $JeaString) {
if ($line -like '*ModulesToImport*') {
$NewJeaFile += [string]::Format(' ModulesToImport = {0}{1}', (ConvertFrom-IcingaArrayToString -Array $JeaConfig.Modules -AddQuotes), "`n");
continue;
}
if ($line -like '*VisibleCmdlets*') {
$NewJeaFile += [string]::Format(' VisibleCmdlets = {0}{1}', (ConvertFrom-IcingaArrayToString -Array $JeaConfig.Cmdlet.Keys -AddQuotes), "`n");
continue;
}
if ($line -like '*VisibleFunctions*') {
$NewJeaFile += [string]::Format(' VisibleFunctions = {0}{1}', (ConvertFrom-IcingaArrayToString -Array $JeaConfig.Function.Keys -AddQuotes), "`n");
continue;
}
$NewJeaFile += $line + "`n";
}
Set-Content -Path (Join-Path -Path (Get-IcingaFrameworkRootPath) -ChildPath 'RoleCapabilities\IcingaForWindows.psrc') -Value $NewJeaFile;
}

View file

@ -14,6 +14,12 @@ if ($null -eq $IcingaEventLogEnums -Or $IcingaEventLogEnums.ContainsKey('Framewo
'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;
};
1001 = @{
'EntryType' = 'Warning';
'Message' = 'Icinga for Windows deprecation warning';
'Details' = 'Icinga for Windows or one of its components executed a function or method, which is flagged as deprecated. Please modify your code or contact the responsible developer to update the component to no longer user this deprecated function or method.';
'EventId' = 1001;
};
1100 = @{
'EntryType' = 'Error';
'Message' = 'Corrupt Icinga for Windows configuration';
@ -32,6 +38,12 @@ if ($null -eq $IcingaEventLogEnums -Or $IcingaEventLogEnums.ContainsKey('Framewo
'Details' = 'Icinga for Windows could not read the specified file after several attempts, because another process is locking the file. Icinga for Windows terminated itself to prevent damage to this file.';
'EventId' = 1102;
};
1400 = @{
'EntryType' = 'Error';
'Message' = 'Icinga for Windows background daemon not found';
'Details' = 'Icinga for Windows could not find the Function or Cmdlet for the specified background daemon. The daemon was not loaded.';
'EventId' = 1400;
};
1500 = @{
'EntryType' = 'Error';
'Message' = 'Failed to securely establish a communication between this server and the client';
@ -44,6 +56,30 @@ if ($null -eq $IcingaEventLogEnums -Or $IcingaEventLogEnums.ContainsKey('Framewo
'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;
};
1502 = @{
'EntryType' = 'Error';
'Message' = 'Unable to create PowerShell RunSpace in JEA context';
'Details' = 'A PowerShell RunSpace for background threads could not be created, as the required Icinga for Windows session configuration file could not be found. Use "Install-IcingaJEAProfile" to resolve this problem.';
'EventId' = 1502;
};
1503 = @{
'EntryType' = 'Error';
'Message' = 'Unable to start Icinga for Windows service';
'Details' = 'Unable to start Icinga for Windows service, as the JEA session created by the service is still active. Run "Restart-IcingaWindowsService" to restart the Icinga for Windows service, while running in JEA context to prevent this issue.';
'EventId' = 1503;
};
1504 = @{
'EntryType' = 'Error';
'Message' = 'Icinga for Windows JEA context vanished';
'Details' = 'The Icinga for Windows JEA session is no longer available. It might have either crashed or get terminated by user actions, like restarting the WinRM service.';
'EventId' = 1504;
};
1505 = @{
'EntryType' = 'Warning';
'Message' = 'Icinga for Windows JEA context not available';
'Details' = 'The Icinga for Windows JEA session is no longer available and is attempted to be restarted on the system. This could have either happenend due to a crash or a user action, like restarting the WinRM service.';
'EventId' = 1505;
};
1550 = @{
'EntryType' = 'Error';
'Message' = 'Unsupported web authentication used';
@ -80,6 +116,12 @@ if ($null -eq $IcingaEventLogEnums -Or $IcingaEventLogEnums.ContainsKey('Framewo
'Details' = 'A web client trying to authenticate failed as the provided user credentials could not be verified.';
'EventId' = 1561;
};
1600 = @{
'EntryType' = 'Error';
'Message' = 'Exception on function calls in JEA context';
'Details' = 'An exception occurred while executing Icinga for Windows code inside a JEA context.';
'EventId' = 1600;
};
}
};
}

View file

@ -0,0 +1,28 @@
function Write-IcingaDeprecated()
{
param (
[string]$Function,
[string]$Argument
);
if ([string]::IsNullOrEmpty($Function)) {
return;
}
$Message = 'The called function or method "{0}" is deprecated. Please update your component or contact the developer to update the component accordingly.';
if ([string]::IsNullOrEmpty($Argument) -eq $FALSE) {
$Message = 'The function or method "{0}" is called with deprecated argument "{1}". Please update your component or contact the developer to update the component accordingly.';
}
Write-IcingaConsoleOutput `
-Message $Message `
-Objects $Function, $Argument `
-ForeColor 'Cyan' `
-Severity 'Deprecated';
Write-IcingaEventMessage -EventId 1001 -Namespace 'Framework' -Objects `
([string]::Format('Command or Method: {0}', $Function)),
([string]::Format('Argument: {0}', $Argument)),
(Get-PSCallStack);
}

View file

@ -16,7 +16,7 @@ function Get-IcingaComponentList()
$SearchList | Add-Member -MemberType NoteProperty -Name 'Components' -Value @{ };
foreach ($entry in $Repositories) {
$RepoContent = Read-IcingaRepositoryFile -Name $entry.Name;
$RepoContent = Read-IcingaRepositoryFile -Name $entry.Name;
if ($null -eq $RepoContent) {
continue;
@ -28,6 +28,10 @@ function Get-IcingaComponentList()
foreach ($repoEntry in $RepoContent.Packages.PSObject.Properties.Name) {
if ($repoEntry.ToLower() -eq 'kickstart') {
continue;
}
$RepoData = New-Object -TypeName PSObject;
$RepoData | Add-Member -MemberType NoteProperty -Name 'Name' -Value $entry.Name;
$RepoData | Add-Member -MemberType NoteProperty -Name 'RemoteSource' -Value $RepoContent.Info.RemoteSource;
@ -45,11 +49,15 @@ function Get-IcingaComponentList()
continue;
}
if ($Snapshot -eq $FALSE -And (Test-Numeric $package.Version.Replace('.', '')) -eq $FALSE) {
continue;
}
if ($SearchList.Components.ContainsKey($repoEntry) -eq $FALSE) {
$SearchList.Components.Add($repoEntry, $package.Version);
}
if ([version]($SearchList.Components[$repoEntry]) -lt [version]$package.Version) {
if ((Test-Numeric $package.Version.Replace('.', '')) -And [version]($SearchList.Components[$repoEntry]) -lt [version]$package.Version) {
$SearchList.Components[$repoEntry] = $package.Version;
}

View file

@ -138,7 +138,7 @@ function Install-IcingaComponent()
}
if ($ManifestFile.ModuleVersion -eq $InstallVersion -And $Force -eq $FALSE) {
Write-IcingaConsoleError ([string]::Format('The package "{0}" with version "{1}" is already installed. Use "-Force" to re-install the component', $Name.ToLower(), $ManifestFile.ModuleVersion));
Write-IcingaConsoleWarning ([string]::Format('The package "{0}" with version "{1}" is already installed. Use "-Force" to re-install the component', $Name.ToLower(), $ManifestFile.ModuleVersion));
Start-Sleep -Seconds 2;
Remove-Item -Path $DownloadDirectory -Recurse -Force;
return;
@ -146,12 +146,13 @@ function Install-IcingaComponent()
# These update steps only apply for the framework
if ($Name.ToLower() -eq 'framework') {
Remove-IcingaFrameworkDependencyFile;
$ServiceStatus = (Get-Service 'icingapowershell' -ErrorAction SilentlyContinue).Status;
$AgentStatus = (Get-Service 'icinga2' -ErrorAction SilentlyContinue).Status;
if ($ServiceStatus -eq 'Running') {
Write-IcingaConsoleNotice 'Stopping Icinga for Windows service';
Stop-IcingaService 'icingapowershell';
Stop-IcingaWindowsService;
Start-Sleep -Seconds 1;
}
if ($AgentStatus -eq 'Running') {
@ -168,7 +169,7 @@ function Install-IcingaComponent()
$ComponentFileContent = Get-ChildItem -Path $ComponentFolder;
foreach ($entry in $ComponentFileContent) {
if (($entry.Name -eq 'cache' -Or $entry.Name -eq 'config') -And $Name.ToLower() -eq 'framework') {
if (($entry.Name -eq 'cache' -Or $entry.Name -eq 'config' -Or $entry.Name -eq 'certificate') -And $Name.ToLower() -eq 'framework') {
continue;
}
@ -197,6 +198,12 @@ function Install-IcingaComponent()
}
Import-Module -Name $ComponentFolder -Force;
# This will ensure that Framework functions will always win over third party functions, overwriting functionality
# of the Framework, which might cause problems during installation otherwise
Import-Module (Join-Path -Path (Get-IcingaForWindowsRootPath) -ChildPath 'icinga-powershell-framework') -Force;
Import-Module (Join-Path -Path (Get-IcingaForWindowsRootPath) -ChildPath 'icinga-powershell-framework') -Global -Force;
Write-IcingaConsoleNotice 'Installation of component "{0}" with version "{1}" was successful. Open a new PowerShell to apply the changes' -Objects $Name.ToLower(), $ManifestFile.ModuleVersion;
} else {
<#
@ -212,6 +219,7 @@ function Install-IcingaComponent()
$ServiceData = Get-IcingaForWindowsServiceData;
$ServiceDirectory = $ServiceData.Directory;
$ServiceUser = $ServiceData.User;
[int]$Success = -1;
if ([string]::IsNullOrEmpty($ConfigDirectory) -eq $FALSE) {
$ServiceDirectory = $ConfigDirectory;
@ -219,6 +227,8 @@ function Install-IcingaComponent()
if ([string]::IsNullOrEmpty($ConfigUser) -eq $FALSE) {
$ServiceUser = $ConfigUser;
} else {
Set-IcingaPowerShellConfig -Path 'Framework.Icinga.ServiceUser' -Value $ServiceUser;
}
foreach ($binary in $FolderContent) {
@ -241,11 +251,8 @@ function Install-IcingaComponent()
$NewService = Read-IcingaServicePackage -File $binary.FullName;
if ($InstalledService.ProductVersion -eq $NewService.ProductVersion -And $null -ne $InstalledService -And $null -ne $NewService -And $Force -eq $FALSE) {
Write-IcingaConsoleError ([string]::Format('The package "service" with version "{0}" is already installed. Use "-Force" to re-install the component', $InstalledService.ProductVersion));
Start-Sleep -Seconds 2;
Remove-Item -Path $DownloadDirectory -Recurse -Force;
return;
$Success = 0;
break;
}
}
@ -254,11 +261,22 @@ function Install-IcingaComponent()
Copy-ItemSecure -Path $binary.FullName -Destination $UpdateBin -Force;
[void](Install-IcingaForWindowsService -Path $ServiceBin -User $ServiceUser -Password (Get-IcingaInternalPowerShellServicePassword));
Update-IcingaServiceUser;
Set-IcingaInternalPowerShellServicePassword -Password $null;
Start-Sleep -Seconds 2;
$Success = 1;
break;
}
if ($Success -eq 0) {
Write-IcingaConsoleWarning ([string]::Format('The package "service" with version "{0}" is already installed. Use "-Force" to re-install the component', $InstalledService.ProductVersion));
Remove-Item -Path $DownloadDirectory -Recurse -Force;
Write-IcingaConsoleNotice 'Installation of component "service" was successful'
return;
}
if ($Success -eq 1) {
Remove-Item -Path $DownloadDirectory -Recurse -Force;
Write-IcingaConsoleNotice 'Installation of component "service" was successful';
return;
}
@ -269,7 +287,7 @@ function Install-IcingaComponent()
return;
} else {
Write-IcingaConsoleError 'There was no manifest file found inside the package';
Remove-Item -Path $DownloadDirectory -Recurse -Force;
Remove-ItemSecure -Path $DownloadDirectory -Recurse -Force;
return;
}
}
@ -295,6 +313,8 @@ function Install-IcingaComponent()
if ([string]::IsNullOrEmpty($ConfigUser) -eq $FALSE) {
$ServiceUser = $ConfigUser;
} else {
Set-IcingaPowerShellConfig -Path 'Framework.Icinga.ServiceUser' -Value $ServiceUser;
}
[string]$InstallFolderMsg = $InstallTarget;
@ -313,7 +333,7 @@ function Install-IcingaComponent()
$MSIData = & powershell.exe -Command { Use-Icinga; return Read-IcingaMSIMetadata -File $args[0] } -Args $DownloadDestination;
if ($InstalledVersion.Full -eq $MSIData.ProductVersion -And $Force -eq $FALSE) {
Write-IcingaConsoleError 'The package "agent" with version "{0}" is already installed. Use "-Force" to re-install the component' -Objects $InstalledVersion.Full;
Write-IcingaConsoleWarning 'The package "agent" with version "{0}" is already installed. Use "-Force" to re-install the component' -Objects $InstalledVersion.Full;
Remove-Item -Path $DownloadDirectory -Recurse -Force;
return;
@ -343,6 +363,7 @@ function Install-IcingaComponent()
}
Set-IcingaAgentServiceUser -User $ServiceUser -SetPermission;
Update-IcingaServiceUser;
Write-IcingaConsoleNotice 'Installation of component "agent" with version "{0}" was successful.' -Objects $MSIData.ProductVersion;
} else {

View file

@ -58,6 +58,23 @@ function Show-Icinga()
$IcingaForWindowsService = Get-IcingaForWindowsServiceData;
$IcingaAgentService = Get-IcingaAgentInstallation;
$WindowsInformation = Get-IcingaWindowsInformation Win32_OperatingSystem | Select-Object Version, BuildNumber, Caption;
$DefinedServiceUser = Get-IcingaPowerShellConfig -Path 'Framework.Icinga.ServiceUser';
$JEAContext = Get-IcingaJEAContext;
$JEASessionFile = Get-IcingaJEASessionFile;
$IcingaForWindowsCert = Get-IcingaForWindowsCertificate;
if ([string]::IsNullOrEmpty($DefinedServiceUser)) {
$DefinedServiceUser = '';
}
if ([string]::IsNullOrEmpty($JEAContext)) {
$JEAContext = '';
}
if ([string]::IsNullOrEmpty($JEASessionFile)) {
$JEASessionFile = '';
}
if ($null -eq $IcingaForWindowsCert -Or [string]::IsNullOrEmpty($IcingaForWindowsCert)) {
$IcingaForWindowsCert = 'Not installed';
}
$Output += '';
$Output += 'Environment configuration';
@ -67,9 +84,17 @@ function Show-Icinga()
$Output += ([string]::Format('Icinga for Windows Service User => {0}', $IcingaForWindowsService.User));
$Output += ([string]::Format('Icinga Agent Path => {0}', $IcingaAgentService.RootDir));
$Output += ([string]::Format('Icinga Agent User => {0}', $IcingaAgentService.User));
$Output += ([string]::Format('Defined Default User => {0}', $DefinedServiceUser));
$Output += ([string]::Format('Icinga Managed User => {0}', (Test-IcingaManagedUser -IcingaUser (Get-IcingaPowerShellConfig -Path 'Framework.Icinga.ServiceUser'))));
$Output += ([string]::Format('PowerShell Version => {0}', $PSVersionTable.PSVersion.ToString()));
$Output += ([string]::Format('Operating System => {0}', $WindowsInformation.Caption));
$Output += ([string]::Format('Operating System Version => {0}', $WindowsInformation.Version));
$Output += ([string]::Format('JEA Context => {0}', $JEAContext));
$Output += ([string]::Format('JEA Session File => {0}', $JEASessionFile));
$Output += '';
$Output += 'Icinga for Windows Certificate';
$Output += '';
$Output += $IcingaForWindowsCert;
$Output += '';
$Output += (Show-IcingaRepository);

View file

@ -1,7 +1,7 @@
function New-IcingaThreadHash()
{
param(
[ScriptBlock]$ShellScript,
[string]$ShellScript,
[array]$Arguments
);

View file

@ -1,9 +1,11 @@
function New-IcingaThreadInstance()
{
param(
param (
[string]$Name,
$ThreadPool,
[ScriptBlock]$ScriptBlock,
[string]$Command,
[hashtable]$CmdParameters,
[array]$Arguments,
[Switch]$Start
);
@ -21,17 +23,53 @@ function New-IcingaThreadInstance()
)
);
$Shell = [PowerShell]::Create();
$Shell.RunspacePool = $ThreadPool;
[void]$Shell.AddScript($ScriptBlock);
foreach ($argument in $Arguments) {
[void]$Shell.AddArgument($argument);
$Shell = [PowerShell]::Create();
$Shell.RunSpacePool = $ThreadPool;
[string]$CodeHash = '';
if ([string]::IsNullOrEmpty($Command) -eq $FALSE) {
[void]$Shell.AddCommand('Use-Icinga');
[void]$Shell.AddParameter('-LibOnly', $TRUE);
[void]$Shell.AddParameter('-Daemon', $TRUE);
[void]$Shell.AddCommand($Command);
$CodeHash = $Command;
foreach ($cmd in $CmdParameters.Keys) {
$Value = $CmdParameters[$cmd];
Write-IcingaDebugMessage -Message 'Adding new argument to thread command' -Objects $cmd, $value, $Command;
[void]$Shell.AddParameter($cmd, $value);
$Arguments += $cmd;
$Arguments += $value;
}
}
if ($null -ne $ScriptBlock) {
Write-IcingaDeprecated -Function 'New-IcingaThreadInstance' -Argument 'ScriptBlock';
$CodeHash = $ScriptBlock;
[void]$Shell.AddScript($ScriptBlock);
foreach ($argument in $Arguments) {
[void]$Shell.AddArgument($argument);
}
}
$Thread = New-Object PSObject;
Add-Member -InputObject $Thread -MemberType NoteProperty -Name Shell -Value $Shell;
if ($Start) {
Add-Member -InputObject $Thread -MemberType NoteProperty -Name Handle -Value ($Shell.BeginInvoke());
Write-IcingaDebugMessage -Message 'Starting shell instance' -Objects $Command, $Shell, $Thread;
try {
$ShellData = $Shell.BeginInvoke();
} catch {
Write-IcingaDebugMessage -Message 'Failed to start Icinga thread instance' -Objects $Command, $_.Exception.Message;
}
Add-Member -InputObject $Thread -MemberType NoteProperty -Name Handle -Value ($ShellData);
Add-Member -InputObject $Thread -MemberType NoteProperty -Name Started -Value $TRUE;
} else {
Add-Member -InputObject $Thread -MemberType NoteProperty -Name Handle -Value $null;
@ -42,7 +80,7 @@ function New-IcingaThreadInstance()
$global:IcingaDaemonData.IcingaThreads.Add($Name, $Thread);
} else {
$global:IcingaDaemonData.IcingaThreads.Add(
(New-IcingaThreadHash -ShellScript $ScriptBlock -Arguments $Arguments),
(New-IcingaThreadHash -ShellScript $CodeHash -Arguments $Arguments),
$Thread
);
}

View file

@ -5,10 +5,23 @@ function New-IcingaThreadPool()
[int]$MaxInstances = 5
);
$SessionConfiguration = $null;
$SessionFile = Get-IcingaJEASessionFile;
if ([string]::IsNullOrEmpty((Get-IcingaJEAContext))) {
$SessionConfiguration = [System.Management.Automation.Runspaces.InitialSessionState]::CreateDefault();
} else {
if ([string]::IsNullOrEmpty($SessionFile)) {
Write-IcingaEventMessage -EventId 1502 -Namespace 'Framework';
return $null;
}
$SessionConfiguration = [System.Management.Automation.Runspaces.InitialSessionState]::CreateFromSessionConfigurationFile($SessionFile);
}
$Runspaces = [RunspaceFactory]::CreateRunspacePool(
$MinInstances,
$MaxInstances,
[System.Management.Automation.Runspaces.InitialSessionState]::CreateDefault(),
$SessionConfiguration,
$host
)

View file

@ -86,7 +86,7 @@ function Get-IcingaCheckCommandConfig()
$CheckName = (Get-Command Invoke-IcingaCheck*).Name
}
[int]$FieldID = 2; # Starts at '2', because '0' and '1' are reserved for 'Verbose' and 'NoPerfData'
[int]$FieldID = 4; # Starts at '4', because 0-3 are reserved for 'Verbose', 'NoPerfData', ExecutionPolicy and a placeholder
[hashtable]$Basket = @{ };
# Define basic hashtable structure by adding fields: "Datafield", "DataList", "Command"
@ -98,21 +98,64 @@ function Get-IcingaCheckCommandConfig()
$Basket.Command.Add(
'PowerShell Base',
@{
'arguments' = @{ };
'arguments' = @{
'-NoProfile' = @{
'order' = '-3';
'set_if' = $TRUE;
};
'-NoLogo' = @{
'order' = '-2';
'set_if' = $TRUE;
};
'-ExecutionPolicy' = @{
'order' = '-1';
'value' = '$IcingaPowerShellBase_String_ExecutionPolicy$';
};
};
'command' = 'C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe';
'disabled' = $FALSE;
'fields' = @();
'fields' = @(
@{
'datafield_id' = 2;
'is_required' = 'n';
'var_filter' = $NULL;
};
);
'imports' = @();
'is_string' = $NULL;
'methods_execute' = 'PluginCheck';
'object_name' = 'PowerShell Base';
'object_type' = 'object';
'timeout' = '180';
'vars' = @{ };
'vars' = @{
'IcingaPowerShellBase_String_ExecutionPolicy' = 'ByPass';
};
'zone' = $NULL;
}
);
Add-PowerShellDataList -Name 'PowerShell ExecutionPolicies' -Basket $Basket -Arguments @( 'AllSigned', 'Bypass', 'Default', 'RemoteSigned', 'Restricted', 'Undefined', 'Unrestricted' );
$Basket.Datafield.Add(
'2', @{
'varname' = 'IcingaPowerShellBase_String_ExecutionPolicy';
'caption' = 'PowerShell Execution Policy';
'description' = 'Defines with which Execution Policy the PowerShell is started';
'datatype' = 'Icinga\Module\Director\DataType\DataTypeDatalist';
'format' = $NULL;
'originalId' = '2';
}
);
$Basket.Datafield['2'].Add(
'settings', @{
'datalist' = 'PowerShell ExecutionPolicies';
'data_type' = 'string';
'behavior' = 'strict';
}
);
$ThresholdIntervalArg = New-Object -TypeName PSObject;
$ThresholdIntervalArg | Add-Member -MemberType NoteProperty -Name 'type' -Value (New-Object -TypeName PSObject);
$ThresholdIntervalArg | Add-Member -MemberType NoteProperty -Name 'Description' -Value (New-Object -TypeName PSObject);
@ -158,13 +201,13 @@ function Get-IcingaCheckCommandConfig()
'-C' = @{
'value' = [string]::Format('try {{ Use-Icinga -Minimal; }} catch {{ Write-Output {1}The Icinga PowerShell Framework is either not installed on the system or not configured properly. Please check https://icinga.com/docs/windows for further details{1}; Write-Output {1}Error:{1} $$($$_.Exception.Message)Components:`r`n$$( Get-Module -ListAvailable {1}icinga-powershell-*{1} )`r`n{1}Module-Path:{1}`r`n$$($$Env:PSModulePath); exit 3; }}; Exit-IcingaExecutePlugin -Command {1}{0}{1} ', $Data.Name, "'");
'order' = '0';
}
};
}
'fields' = @();
'imports' = @( 'PowerShell Base' );
'object_name' = $Data.Name;
'object_type' = 'object';
'vars' = @{};
'vars' = @{ };
}
);
@ -351,7 +394,7 @@ function Get-IcingaCheckCommandConfig()
$CheckParamList = @( $ThresholdIntervalArg );
foreach ($entry in $Data.parameters.parameter) {
$CheckParamList += (Convert-IcingaCheckArgumentToPSObject -Parameter $entry);;
$CheckParamList += (Convert-IcingaCheckArgumentToPSObject -Parameter $entry);
}
foreach ($parameter in $CheckParamList) {
@ -569,6 +612,21 @@ function Write-IcingaPlainConfigurationFiles()
$PowerShellBase += [string]::Format(' "C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe"{0}', (New-IcingaNewLine));
$PowerShellBase += [string]::Format(' ]{0}', (New-IcingaNewLine));
$PowerShellBase += [string]::Format(' timeout = 3m{0}', (New-IcingaNewLine));
$PowerShellBase += [string]::Format(' arguments += {{{0}', (New-IcingaNewLine));
$PowerShellBase += [string]::Format(' "-ExecutionPolicy" = {{{0}', (New-IcingaNewLine));
$PowerShellBase += [string]::Format(' order = -1{0}', (New-IcingaNewLine));
$PowerShellBase += [string]::Format(' value = "$IcingaPowerShellBase_String_ExecutionPolicy$"{0}', (New-IcingaNewLine));
$PowerShellBase += [string]::Format(' }}{0}', (New-IcingaNewLine));
$PowerShellBase += [string]::Format(' "-NoLogo" = {{{0}', (New-IcingaNewLine));
$PowerShellBase += [string]::Format(' order = -2{0}', (New-IcingaNewLine));
$PowerShellBase += [string]::Format(' set_if = "1"{0}', (New-IcingaNewLine));
$PowerShellBase += [string]::Format(' }}{0}', (New-IcingaNewLine));
$PowerShellBase += [string]::Format(' "-NoProfile" = {{{0}', (New-IcingaNewLine));
$PowerShellBase += [string]::Format(' order = -3{0}', (New-IcingaNewLine));
$PowerShellBase += [string]::Format(' set_if = "1"{0}', (New-IcingaNewLine));
$PowerShellBase += [string]::Format(' }}{0}', (New-IcingaNewLine));
$PowerShellBase += [string]::Format(' }}{0}', (New-IcingaNewLine));
$PowerShellBase += [string]::Format(' vars.IcingaPowerShellBase_String_ExecutionPolicy = "ByPass"{0}', (New-IcingaNewLine));
$PowerShellBase += '}';
Write-IcingaFileSecure -File (Join-Path -Path $ConfigDirectory -ChildPath 'PowerShell_Base.conf') -Value $PowerShellBase;

View file

@ -0,0 +1,32 @@
function Get-IcingaFileHash()
{
param (
[string]$Path,
[ValidateSet('SHA1', 'SHA256', 'SHA384', 'SHA512', 'MD5')]
[string]$Algorithm = 'SHA256'
);
if ([string]::IsNullOrEmpty($Path) -Or ((Test-Path -Path $Path) -eq $FALSE)) {
Write-IcingaConsoleError 'Your path is either not specified or does not exist';
return $null;
}
$FileHasher = New-Object "System.Security.Cryptography.${Algorithm}CryptoServiceProvider";
if ($null -eq $FileHasher) {
Write-IcingaConsoleError 'Unable to create cryptography objects for algorithm "{0}"' -Objects $Algorithm;
return $null;
}
# Read the file specified in $FilePath as a Byte array
[System.IO.Stream]$FileStream = [System.IO.File]::OpenRead($Path)
[Byte[]]$FileHash = $FileHasher.ComputeHash($FileStream)
[string]$HashString = [BitConverter]::ToString($FileHash).Replace('-', '');
$RetValue = New-Object -TypeName PSObject;
$RetValue | Add-Member -MemberType NoteProperty -Name 'Algorithm' -Value $Algorithm.ToUpper();
$RetValue | Add-Member -MemberType NoteProperty -Name 'Hash' -Value $HashString;
$RetValue | Add-Member -MemberType NoteProperty -Name 'Path' -Value $Path;
return $RetValue;
}

View file

@ -4,6 +4,10 @@ function Get-IcingaUserSID()
[string]$User
);
if ([string]::IsNullOrEmpty($User)) {
return $null;
}
if ($User -eq 'LocalSystem') {
$User = 'NT Authority\SYSTEM';
}
@ -14,7 +18,14 @@ function Get-IcingaUserSID()
$NTUser = New-Object System.Security.Principal.NTAccount($UserData.Domain, $UserData.User);
$SecurityData = $NTUser.Translate([System.Security.Principal.SecurityIdentifier]);
} catch {
throw $_.Exception;
try {
# Try again but this time with our domain
$UserData.Domain = (Get-IcingaWindowsInformation -ClassName Win32_ComputerSystem).Domain;
$NTUser = New-Object System.Security.Principal.NTAccount($UserData.Domain, $UserData.User);
$SecurityData = $NTUser.Translate([System.Security.Principal.SecurityIdentifier]);
} catch {
throw $_.Exception;
}
}
if ($null -eq $SecurityData) {

View file

@ -0,0 +1,16 @@
function Get-IcingaUsernameFromSID()
{
param (
[string]$SID
);
if ([string]::IsNullOrEmpty($SID)) {
Write-IcingaConsoleError 'You have to specify a SID';
return $null;
}
$UserData = New-Object System.Security.Principal.SecurityIdentifier $SID;
$UserObject = $UserData.Translate([System.Security.Principal.NTAccount]);
return $UserObject.Value;
}

View file

@ -0,0 +1,12 @@
function New-IcingaVersionObject()
{
param (
[array]$Version
);
if ($null -eq $Version -Or $Version.Count -gt 4) {
return (New-Object System.Version);
}
return (New-Object System.Version $Version);
}

View file

@ -9,5 +9,5 @@ function Test-PSCustomObjectMember()
return $FALSE;
}
return ([bool]($PSObject.PSobject.Properties.Name -eq $Name));
return ([bool]($PSObject.PSObject.Properties.Name -eq $Name));
}

View file

@ -0,0 +1,39 @@
function Write-IcingaProgressStatus()
{
param (
[int]$CurrentValue = 0,
[int]$MaxValue = 1,
[string]$Message = 'Processing Icinga for Windows',
[string]$Status = "{0}% Complete",
[switch]$Details = $FALSE
);
if ($CurrentValue -le -99) {
$CurrentValue = 0;
return;
}
if ($MaxValue -le 0) {
$MaxValue = 1;
}
$ProgressValue = [math]::Round($CurrentValue / $MaxValue * 100, 0);
if ($Details) {
$Message = [string]::Format('{0}: {1}/{2}', $Message, $CurrentValue, $MaxValue);
}
$ProgressPreference = 'Continue';
if ($ProgressValue -ge 100) {
$ProgressValue = 100;
Write-Progress -Activity $Message -Status ([string]::Format($Status, $ProgressValue)) -PercentComplete $ProgressValue -Completed;
$CurrentValue = -99;
return $CurrentValue;
}
Write-Progress -Activity $Message -Status ([string]::Format($Status, $ProgressValue)) -PercentComplete $ProgressValue;
return ($CurrentValue += 1);
}

View file

@ -0,0 +1,12 @@
function Clear-IcingaWindowsUserPassword()
{
if ($null -eq $Global:Icinga) {
return;
}
if ($Global:Icinga.ContainsKey('ServiceUserPassword') -eq $FALSE) {
return;
}
$Global:Icinga.ServiceUserPassword = $null;
}

View file

@ -0,0 +1,23 @@
function Get-IcingaRandomChars()
{
param (
[int]$Count = 10,
[string]$Symbols = 'abcdefghiklmnoprstuvwxyzABCDEFGHKLMNOPRSTUVWXYZ1234567890!§$%&/()=?}][{@#*+'
);
$RandomChars = '';
if ([string]::IsNullOrEmpty($Symbols)) {
return $RandomChars;
}
while ($Count -gt 0) {
[int]$SymbolLength = $Symbols.Length;
$RandomValue = Get-Random -Minimum 0 -Maximum ($SymbolLength - 1);
$RandomChars += $Symbols[$RandomValue];
$Count -= 1;
}
return $RandomChars;
}

View file

@ -0,0 +1,7 @@
function Get-IcingaWindowsUserMetadata()
{
return @{
'Description' = 'Dedicated user for Icinga for Windows with limited privileges. The user is only allowed to be used as service user, while local login or RDP sessions are disabled. For monitoring, this user requires a valid JEA profile.';
'FullName' = 'Icinga for Windows Monitoring User';
};
}

View file

@ -0,0 +1,19 @@
function Install-IcingaSecurity()
{
param (
[string]$IcingaUser = 'icinga',
[switch]$RebuildFramework = $FALSE,
[switch]$AllowScriptBlocks = $FALSE,
[switch]$ConstrainedLanguage = $FALSE
);
if ($PSVersionTable.PSVersion -lt (New-IcingaVersionObject -Version 5, 0)) {
Write-IcingaConsoleError 'You cannot use JEA profiles on your system, as your installed PowerShell version "{0}" is lower than minimum required version "5.0"' -Objects $PSVersionTable.PSVersion;
return;
}
Install-IcingaServiceUser -IcingaUser $IcingaUser;
Install-IcingaJEAProfile -IcingaUser $IcingaUser -RebuildFramework:$RebuildFramework -AllowScriptBlocks:$AllowScriptBlocks -ConstrainedLanguage:$ConstrainedLanguage;
Restart-IcingaWindowsService;
}

View file

@ -0,0 +1,33 @@
function Install-IcingaServiceUser()
{
param (
$IcingaUser = 'icinga'
);
if ([string]::IsNullOrEmpty($IcingaUser)) {
Write-IcingaConsoleError 'The provided user cannot be empty.';
return;
}
Write-IcingaConsoleNotice 'Installing user "{0}"' -Objects $IcingaUser;
$User = New-IcingaWindowsUser -IcingaUser $IcingaUser;
Start-Sleep -Seconds 2;
Set-IcingaPowerShellConfig -Path 'Framework.Icinga.ServiceUser' -Value $User.User;
Set-IcingaServiceUser -User $IcingaUser -Password $Global:Icinga.ServiceUserPassword -Service 'icinga2' | Out-Null;
Set-IcingaServiceUser -User $IcingaUser -Password $Global:Icinga.ServiceUserPassword -Service 'icingapowershell' | Out-Null;
Update-IcingaWindowsUserPermission -SID $User.SID;
Set-IcingaUserPermissions -IcingaUser $IcingaUser;
Restart-IcingaService 'icinga2';
Restart-IcingaWindowsService;
Clear-IcingaWindowsUserPassword;
Write-IcingaConsoleNotice 'User "{0}" including permissions was successfully installed on this host' -Objects $IcingaUser;
}

View file

@ -0,0 +1,72 @@
function New-IcingaWindowsUser()
{
param (
$IcingaUser = 'icinga'
);
if ((Test-AdministrativeShell) -eq $FALSE) {
Write-IcingaConsoleError 'For this command you require to run an Admin shell';
return @{
'User' = $null;
'SID' = $null;
};
}
$UserMetadata = Get-IcingaWindowsUserMetadata;
$UserConfig = Get-IcingaWindowsInformation -Class 'Win32_UserAccount' | Where-Object { $_.Name -eq $IcingaUser };
if ($null -ne $UserConfig) {
# User already exist -> override password - but only if the user is entirely managed by Icinga
if ($UserConfig.FullName -eq $UserMetadata.FullName -And $UserConfig.Description -eq $UserMetadata.Description) {
$Result = Start-IcingaProcess -Executable 'net' -Arguments ([string]::Format('user "{0}" "{1}"', $IcingaUser, (ConvertFrom-IcingaSecureString -SecureString (New-IcingaWindowsUserPassword))));
if ($Result.ExitCode -ne 0) {
Write-IcingaConsoleError 'Failed to update password for user "{0}": {1}' -Objects $IcingaUser, $Result.Error;
return @{
'User' = $UserConfig.Caption;
'SID' = $UserConfig.SID;
};
}
Write-IcingaConsoleNotice 'User updated successfully.';
}
return @{
'User' = $UserConfig.Caption;
'SID' = $UserConfig.SID;
};
}
# Access our local Account Database
$AccountDB = [ADSI]"WinNT://$Env:COMPUTERNAME,Computer";
$IcingaUserObject = $AccountDB.Create("User", $IcingaUser);
$IcingaUserObject.SetPassword((ConvertFrom-IcingaSecureString -SecureString (New-IcingaWindowsUserPassword)));
$IcingaUserObject.SetInfo();
$IcingaUserObject.FullName = $UserMetadata.FullName;
$IcingaUserObject.SetInfo();
$IcingaUserObject.Description = $UserMetadata.Description;
$IcingaUserObject.SetInfo();
$IcingaUserObject.UserFlags = 65600;
$IcingaUserObject.SetInfo();
# Add to local user group
<# This is not required, but let's leave it here for possible later lookup on how this works
$SIDLocalGroup = New-Object System.Security.Principal.SecurityIdentifier ("S-1-5-32-545");
$LocalGroup = ($SIDLocalGroup.Translate([System.Security.Principal.NTAccount])).Value.Split('\')[1];
$LocalUserGroup = [ADSI]"WinNT://$Env:COMPUTERNAME/$LocalGroup,group";
$LocalUserGroup.Add("WinNT://$Env:COMPUTERNAME/$IcingaUser,user")
#>
$UserConfig = Get-IcingaWindowsInformation -Class 'Win32_UserAccount' | Where-Object { $_.Name -eq $IcingaUser };
Write-IcingaConsoleNotice 'User was successfully created.';
return @{
'User' = $UserConfig.Caption;
'SID' = $UserConfig.SID;
};
}

View file

@ -0,0 +1,17 @@
function New-IcingaWindowsUserPassword()
{
if ($null -eq $Global:Icinga) {
$Global:Icinga = @{
'ServiceUserPassword' = $null
};
}
if ($Global:Icinga.ContainsKey('ServiceUserPassword') -eq $FALSE) {
$Global:Icinga.Add('ServiceUserPassword', $null);
}
[SecureString]$Password = ConvertTo-IcingaSecureString -String (Get-IcingaRandomChars -Count 60);
$Global:Icinga.ServiceUserPassword = $Password;
return $Password;
}

View file

@ -0,0 +1,28 @@
function Remove-IcingaWindowsUser()
{
param (
$IcingaUser = 'icinga'
);
$UserConfig = Get-IcingaWindowsInformation -Class 'Win32_UserAccount' | Where-Object { $_.Name -eq $IcingaUser };
if ((Test-IcingaManagedUser -IcingaUser $IcingaUser) -eq $FALSE) {
Write-IcingaConsoleNotice 'The user "{0}" is not present or not created by Icinga for Windows. Unable to remove user' -Objects $IcingaUser;
return;
}
$Result = Start-IcingaProcess -Executable 'net' -Arguments ([string]::Format('user "{0}" /DELETE', $IcingaUser));
if ($Result.ExitCode -ne 0) {
Write-IcingaConsoleError 'Failed to delete user "{0}": {1}' -Objects $IcingaUser, $Result.Error;
} else {
# Delete Home Directory
$HomePath = Join-Path -Path ($ENV:HOMEDRIVE) -ChildPath (Join-Path -Path '\Users\' -ChildPath $IcingaUser);
Remove-ItemSecure -Path $HomePath -Recurse -Force;
}
return @{
'User' = $UserConfig.Caption;
'SID' = $UserConfig.SID;
};
}

View file

@ -0,0 +1,12 @@
function Restart-IcingaWindowsService()
{
[string]$JeaPid = Get-IcingaJEAServicePid;
Stop-IcingaService -Service 'icingapowershell';
if ((Test-IcingaJEAServiceRunning -JeaPid $JeaPid)) {
Stop-Process -Id $JeaPid -Force;
}
Restart-IcingaService -Service 'icingapowershell';
}

View file

@ -0,0 +1,10 @@
function Stop-IcingaWindowsService()
{
[string]$JeaPid = Get-IcingaJEAServicePid;
Stop-IcingaService -Service 'icingapowershell';
if ((Test-IcingaJEAServiceRunning -JeaPid $JeaPid)) {
Stop-Process -Id $JeaPid -Force;
}
}

View file

@ -0,0 +1,27 @@
function Test-IcingaManagedUser()
{
param (
[string]$IcingaUser,
[string]$SID
);
$UserData = Get-IcingaWindowsInformation -Class 'Win32_UserAccount' | Where-Object { $_.Name -eq $IcingaUser };
$FullUserData = Get-IcingaWindowsInformation -Class 'Win32_UserAccount' | Where-Object { $_.Caption.ToLower() -eq $IcingaUser.ToLower() };
if ($null -eq $FullUserData -And $null -eq $UserData -And [string]::IsNullOrEmpty($SID)) {
return $FALSE;
}
if ([string]::IsNullOrEmpty($SID)) {
$SID = Get-IcingaUserSID -User $IcingaUser;
}
$UserConfig = Get-IcingaWindowsInformation -Class 'Win32_UserAccount' | Where-Object { $_.SID -eq $SID };
$UserMetadata = Get-IcingaWindowsUserMetadata;
if ($null -eq $UserConfig -Or $UserConfig.FullName -ne $UserMetadata.FullName -Or $UserConfig.Description -ne $UserMetadata.Description) {
return $FALSE;
}
return $TRUE;
}

View file

@ -0,0 +1,9 @@
function Uninstall-IcingaSecurity()
{
param (
$IcingaUser = 'icinga'
);
Uninstall-IcingaServiceUser -IcingaUser $IcingaUser;
Uninstall-IcingaJEAProfile;
}

View file

@ -0,0 +1,31 @@
function Uninstall-IcingaServiceUser()
{
param (
$IcingaUser = 'icinga'
);
if ([string]::IsNullOrEmpty($IcingaUser)) {
Write-IcingaConsoleError 'The provided user cannot be empty.';
return;
}
Write-IcingaConsoleNotice 'Uninstalling user "{0}"' -Objects $IcingaUser;
Stop-IcingaService 'icinga2';
Stop-IcingaWindowsService;
Set-IcingaPowerShellConfig -Path 'Framework.Icinga.ServiceUser' -Value '';
Set-IcingaServiceUser -User 'NT Authority\NetworkService' -Service 'icinga2' | Out-Null;
Set-IcingaServiceUser -User 'NT Authority\NetworkService' -Service 'icingapowershell' | Out-Null;
Set-IcingaUserPermissions -IcingaUser $IcingaUser -Remove;
$UserConfig = Remove-IcingaWindowsUser -IcingaUser $IcingaUser;
Update-IcingaWindowsUserPermission -SID $UserConfig.SID -Remove;
Restart-IcingaService 'icinga2';
Restart-IcingaWindowsService;
Write-IcingaConsoleNotice 'User "{0}" including permissions was removed from this host' -Objects $IcingaUser;
}

View file

@ -0,0 +1,22 @@
function Update-IcingaServiceUser()
{
$IcingaUser = Get-IcingaPowerShellConfig -Path 'Framework.Icinga.ServiceUser';
if ([string]::IsNullOrEmpty($IcingaUser)) {
return;
}
if ((Test-IcingaManagedUser -IcingaUser $IcingaUser) -eq $FALSE) {
return;
}
$SID = Get-IcingaUserSID -User $IcingaUser;
$UserConfig = Get-IcingaWindowsInformation -Class 'Win32_UserAccount' | Where-Object { $_.SID -eq $SID };
$User = New-IcingaWindowsUser -IcingaUser $UserConfig.Name;
Set-IcingaServiceUser -User $IcingaUser -Password $Global:Icinga.ServiceUserPassword -Service 'icinga2' | Out-Null;
Set-IcingaServiceUser -User $IcingaUser -Password $Global:Icinga.ServiceUserPassword -Service 'icingapowershell' | Out-Null;
Restart-IcingaService 'icinga2';
Restart-IcingaWindowsService;
}

View file

@ -0,0 +1,74 @@
function Update-IcingaWindowsUserPermission()
{
param (
[string]$SID = '',
[switch]$Remove = $FALSE
);
if ([string]::IsNullOrEmpty($SID)) {
Write-IcingaConsoleError 'You have to specify the SID of the user to set the security profile to';
return;
}
if ($SID.Length -le 16) {
Write-IcingaConsoleWarning 'It seems the provided SID "{0}" is a system SID. Skipping permission update' -Objects $SID;
return;
}
if ((Test-IcingaManagedUser -SID $SID) -eq $FALSE) {
Write-IcingaConsoleWarning 'This user is not managed by Icinga directly. Skipping permission update';
return;
}
$UpdatedProfile = New-IcingaTemporaryFile;
$SystemOutput = Start-IcingaProcess -Executable 'secedit.exe' -Arguments ([string]::Format('/export /cfg "{0}.inf"', $UpdatedProfile));
$NewSecurityProfile = @();
if ($SystemOutput.ExitCode -ne 0) {
throw ([string]::Format('Unable to fetch security profile: {0}', $SystemOutput.Message));
return;
}
$SecurityProfile = '';
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);
}
$NewSecurityProfile += $line;
}
} else {
$SecurityProfile = Get-Content "$UpdatedProfile.inf" -Raw;
$SecurityProfile = $SecurityProfile.Replace([string]::Format(',*{0}', $SID), '');
$SecurityProfile = $SecurityProfile.Replace([string]::Format('*{0},', $SID), '');
$NewSecurityProfile = $SecurityProfile;
}
Set-Content -Path "$UpdatedProfile.inf" -Value $NewSecurityProfile;
$SystemOutput = Start-IcingaProcess -Executable 'secedit.exe' -Arguments ([string]::Format('/import /cfg "{0}.inf" /db "{0}.sdb"', $UpdatedProfile));
if ($SystemOutput.ExitCode -ne 0) {
throw ([string]::Format('Unable to import security profile: {0}', $SystemOutput.Message));
return;
}
$SystemOutput = Start-IcingaProcess -Executable 'secedit.exe' -Arguments ([string]::Format('/configure /cfg "{0}.inf" /db "{0}.sdb"', $UpdatedProfile));
if ($SystemOutput.ExitCode -ne 0) {
throw ([string]::Format('Unable to configure security profile: {0}', $SystemOutput.Message));
return;
}
Remove-Item $UpdatedProfile*;
}

View file

@ -0,0 +1,32 @@
function Add-IcingaForWindowsDaemon()
{
param (
$IcingaDaemonData
);
Use-Icinga -LibOnly -Daemon;
$Global:IcingaDaemonData = $IcingaDaemonData;
try {
$EnabledDaemons = Get-IcingaBackgroundDaemons;
foreach ($daemon in $EnabledDaemons.Keys) {
Write-IcingaDebugMessage -Message 'Trying to enable background daemon' -Objects $daemon;
if (-Not (Test-IcingaFunction $daemon)) {
Write-IcingaEventMessage -EventId 1400 -Namespace 'Framework' $daemon;
continue;
}
$daemonArgs = $EnabledDaemons[$daemon];
Write-IcingaDebugMessage -Message 'Starting background daemon' -Objects $daemon, $daemonArgs;
& $daemon @daemonArgs;
}
} catch {
# Todo: Add exception handling
}
while ($TRUE) {
Start-Sleep -Seconds 1;
}
}

View file

@ -1,44 +1,96 @@
function Start-IcingaPowerShellDaemon()
{
param(
[switch]$RunAsService
param (
[switch]$RunAsService = $FALSE,
[switch]$JEARestart = $FALSE
);
$ScriptBlock = {
param($IcingaDaemonData);
Use-Icinga -LibOnly -Daemon;
try {
$EnabledDaemons = Get-IcingaBackgroundDaemons;
foreach ($daemon in $EnabledDaemons.Keys) {
if (-Not (Test-IcingaFunction $daemon)) {
continue;
}
$daemonArgs = $EnabledDaemons[$daemon];
&$daemon @daemonArgs;
}
} catch {
# Todo: Add exception handling
}
while ($TRUE) {
Start-Sleep -Seconds 1;
}
};
Use-Icinga;
$global:IcingaDaemonData.FrameworkRunningAsDaemon = $TRUE;
$global:IcingaDaemonData.Add('BackgroundDaemon', [hashtable]::Synchronized(@{}));
# Todo: Add config for active background tasks. Set it to 20 for the moment
$global:IcingaDaemonData.IcingaThreadPool.Add('BackgroundPool', (New-IcingaThreadPool -MaxInstances 20));
$global:IcingaDaemonData.Add('Config', (Read-IcingaPowerShellConfig));
New-IcingaThreadInstance -Name "Icinga_PowerShell_Background_Daemon" -ThreadPool $IcingaDaemonData.IcingaThreadPool.BackgroundPool -ScriptBlock $ScriptBlock -Arguments @( $global:IcingaDaemonData ) -Start;
[string]$MainServicePidFile = (Join-Path -Path (Get-IcingaCacheDir) -ChildPath 'service.pid');
[string]$JeaPidFile = (Join-Path -Path (Get-IcingaCacheDir) -ChildPath 'jea.pid');
[string]$JeaProfile = Get-IcingaPowerShellConfig -Path 'Framework.JEAProfile';
[Security.Cryptography.X509Certificates.X509Certificate2]$Certificate = Get-IcingaForWindowsCertificate;
[string]$JeaPid = '';
if (Test-IcingaJEAServiceRunning) {
Write-IcingaEventMessage -EventId 1503 -Namespace 'Framework';
exit 1;
}
Write-IcingaFileSecure -File ($MainServicePidFile) -Value $PID;
if ([string]::IsNullOrEmpty($JeaProfile)) {
Write-IcingaDebugMessage -Message 'Starting Icinga for Windows service without JEA context' -Objects $RunAsService, $JEARestart, $JeaProfile;
$global:IcingaDaemonData.FrameworkRunningAsDaemon = $TRUE;
$global:IcingaDaemonData.Add('BackgroundDaemon', [hashtable]::Synchronized(@{ }));
# Todo: Add config for active background tasks. Set it to 20 for the moment
$global:IcingaDaemonData.IcingaThreadPool.Add('BackgroundPool', (New-IcingaThreadPool -MaxInstances 20));
$global:IcingaDaemonData.Add('SSLCertificate', $Certificate);
New-IcingaThreadInstance -Name "Icinga_PowerShell_Background_Daemon" -ThreadPool $IcingaDaemonData.IcingaThreadPool.BackgroundPool -Command 'Add-IcingaForWindowsDaemon' -CmdParameters @{ 'IcingaDaemonData' = $global:IcingaDaemonData } -Start;
} else {
Write-IcingaDebugMessage -Message 'Starting Icinga for Windows service inside JEA context' -Objects $RunAsService, $JEARestart, $JeaProfile;
& powershell.exe -NoProfile -NoLogo -ConfigurationName $JeaProfile -Command {
try {
Use-Icinga;
Write-IcingaFileSecure -File ($args[1]) -Value $PID;
$Global:IcingaDaemonData.JEAContext = $TRUE;
$global:IcingaDaemonData.FrameworkRunningAsDaemon = $TRUE;
$global:IcingaDaemonData.Add('BackgroundDaemon', [hashtable]::Synchronized(@{ }));
# Todo: Add config for active background tasks. Set it to 20 for the moment
$global:IcingaDaemonData.IcingaThreadPool.Add('BackgroundPool', (New-IcingaThreadPool -MaxInstances 20));
$global:IcingaDaemonData.Add('SSLCertificate', ($args[0]));
New-IcingaThreadInstance -Name "Icinga_PowerShell_Background_Daemon" -ThreadPool $IcingaDaemonData.IcingaThreadPool.BackgroundPool -Command 'Add-IcingaForWindowsDaemon' -CmdParameters @{ 'IcingaDaemonData' = $global:IcingaDaemonData } -Start;
while ($TRUE) {
Start-Sleep -Seconds 100;
}
} catch {
$CallStack = @();
foreach ($entry in (Get-PSCallStack)) {
$CallStack += [string]::Format('{0} => Line {1}', $entry.FunctionName, $entry.ScriptLineNumber);
}
Write-IcingaEventMessage -EventId 1600 -Namespace Framework -Objects $_.Exception.Message, $_.Exception.StackTrace, $CallStack;
}
} -Args $Certificate, $JeaPidFile;
}
if ($JEARestart) {
return;
}
if ($RunAsService) {
[int]$JeaRestartCounter = 1;
while ($TRUE) {
if ([string]::IsNullOrEmpty($JeaProfile) -eq $FALSE) {
if ([string]::IsNullOrEmpty($JeaPid)) {
[string]$JeaPid = Get-IcingaJEAServicePid;
}
if ((Test-IcingaJEAServiceRunning -JeaPid $JeaPid) -eq $FALSE) {
if ($JeaRestartCounter -gt 5) {
Write-IcingaEventMessage -EventId 1504 -Namespace Framework;
exit 1;
}
Write-IcingaFileSecure -File $JeaPidFile -Value '';
Write-IcingaEventMessage -EventId 1505 -Namespace Framework -Objects ([string]::Format('{0}/5', $JeaRestartCounter));
Start-IcingaPowerShellDaemon -RunAsService:$RunAsService -JEARestart;
$JeaRestartCounter += 1;
$JeaPid = '';
}
Start-Sleep -Seconds 5;
continue;
}
Start-Sleep -Seconds 100;
}
}

View file

@ -0,0 +1,164 @@
function Add-IcingaServiceCheckTask()
{
param (
$IcingaDaemonData,
$CheckCommand,
$Arguments,
$Interval,
$TimeIndexes,
$CheckId
);
Use-Icinga -LibOnly -Daemon;
$PassedTime = 0;
$SortedResult = $null;
$PerfCache = @{ };
$AverageCalc = @{ };
[int]$MaxTime = 0;
# Initialise some global variables we use to actually store check result data from
# plugins properly. This is doable from each thread instance as this part isn't
# shared between daemons
New-IcingaCheckSchedulerEnvironment;
foreach ($index in $TimeIndexes) {
# Only allow numeric index values
if ((Test-Numeric $index) -eq $FALSE) {
continue;
}
if ($AverageCalc.ContainsKey([string]$index) -eq $FALSE) {
$AverageCalc.Add(
[string]$index,
@{
'Interval' = ([int]$index);
'Time' = ([int]$index * 60);
'Sum' = 0;
'Count' = 0;
}
);
}
if ($MaxTime -le [int]$index) {
$MaxTime = [int]$index;
}
}
[int]$MaxTimeInSeconds = $MaxTime * 60;
if (-Not ($global:Icinga.CheckData.ContainsKey($CheckCommand))) {
$global:Icinga.CheckData.Add($CheckCommand, @{ });
$global:Icinga.CheckData[$CheckCommand].Add('results', @{ });
$global:Icinga.CheckData[$CheckCommand].Add('average', @{ });
}
$LoadedCacheData = Get-IcingaCacheData -Space 'sc_daemon' -CacheStore 'checkresult_store' -KeyName $CheckCommand;
if ($null -ne $LoadedCacheData) {
foreach ($entry in $LoadedCacheData.PSObject.Properties) {
$global:Icinga.CheckData[$CheckCommand]['results'].Add(
$entry.name,
@{ }
);
foreach ($item in $entry.Value.PSObject.Properties) {
$global:Icinga.CheckData[$CheckCommand]['results'][$entry.name].Add(
$item.Name,
$item.Value
);
}
}
}
while ($TRUE) {
if ($PassedTime -ge $Interval) {
try {
& $CheckCommand @Arguments | Out-Null;
} catch {
# Just for debugging. Not required in production or usable at all
$ErrMsg = $_.Exception.Message;
Write-IcingaConsoleError $ErrMsg;
}
try {
$UnixTime = Get-IcingaUnixTime;
foreach ($result in $global:Icinga.CheckData[$CheckCommand]['results'].Keys) {
[string]$HashIndex = $result;
$SortedResult = $global:Icinga.CheckData[$CheckCommand]['results'][$HashIndex].GetEnumerator() | Sort-Object name -Descending;
Add-IcingaHashtableItem -Hashtable $PerfCache -Key $HashIndex -Value @{ } | Out-Null;
foreach ($timeEntry in $SortedResult) {
if ((Test-Numeric $timeEntry.Value) -eq $FALSE) {
continue;
}
foreach ($calc in $AverageCalc.Keys) {
if (($UnixTime - $AverageCalc[$calc].Time) -le [int]$timeEntry.Key) {
$AverageCalc[$calc].Sum += $timeEntry.Value;
$AverageCalc[$calc].Count += 1;
}
}
if (($UnixTime - $MaxTimeInSeconds) -le [int]$timeEntry.Key) {
Add-IcingaHashtableItem -Hashtable $PerfCache[$HashIndex] -Key ([string]$timeEntry.Key) -Value ([string]$timeEntry.Value) | Out-Null;
}
}
foreach ($calc in $AverageCalc.Keys) {
if ($AverageCalc[$calc].Count -ne 0) {
$AverageValue = ($AverageCalc[$calc].Sum / $AverageCalc[$calc].Count);
[string]$MetricName = Format-IcingaPerfDataLabel (
[string]::Format('{0}_{1}', $HashIndex, $AverageCalc[$calc].Interval)
);
Add-IcingaHashtableItem `
-Hashtable $global:Icinga.CheckData[$CheckCommand]['average'] `
-Key $MetricName -Value $AverageValue -Override | Out-Null;
}
$AverageCalc[$calc].Sum = 0;
$AverageCalc[$calc].Count = 0;
}
}
# Flush data we no longer require in our cache to free memory
[array]$CheckStores = $global:Icinga.CheckData[$CheckCommand]['results'].Keys;
foreach ($CheckStore in $CheckStores) {
[string]$CheckKey = $CheckStore;
[array]$CheckTimeStamps = $global:Icinga.CheckData[$CheckCommand]['results'][$CheckKey].Keys;
foreach ($TimeSample in $CheckTimeStamps) {
if (($UnixTime - $MaxTimeInSeconds) -gt [int]$TimeSample) {
Remove-IcingaHashtableItem -Hashtable $global:Icinga.CheckData[$CheckCommand]['results'][$CheckKey] -Key ([string]$TimeSample);
}
}
}
Set-IcingaCacheData -Space 'sc_daemon' -CacheStore 'checkresult' -KeyName $CheckCommand -Value $global:Icinga.CheckData[$CheckCommand]['average'];
# Write collected metrics to disk in case we reload the daemon. We will load them back into the module after reload then
Set-IcingaCacheData -Space 'sc_daemon' -CacheStore 'checkresult_store' -KeyName $CheckCommand -Value $PerfCache;
} catch {
# Just for debugging. Not required in production or usable at all
$ErrMsg = $_.Exception.Message;
Write-IcingaConsoleError 'Failed to handle check result processing: {0}' -Objects $ErrMsg;
}
# Cleanup the error stack and remove not required data
$Error.Clear();
# Always ensure our check data is cleared regardless of possible
# exceptions which might occur
Get-IcingaCheckSchedulerPerfData | Out-Null;
Get-IcingaCheckSchedulerPluginOutput | Out-Null;
$PassedTime = 0;
$SortedResult.Clear();
$PerfCache.Clear();
}
$PassedTime += 1;
Start-Sleep -Seconds 1;
# Force PowerShell to call the garbage collector to free memory
[System.GC]::Collect();
}
}

View file

@ -22,42 +22,45 @@
function Start-IcingaServiceCheckDaemon()
{
$ScriptBlock = {
param($IcingaDaemonData);
New-IcingaThreadInstance -Name "Icinga_PowerShell_ServiceCheck_Scheduler" -ThreadPool $IcingaDaemonData.IcingaThreadPool.BackgroundPool -Command 'Add-IcingaServiceCheckDaemon' -CmdParameters @{ 'IcingaDaemonData' = $global:IcingaDaemonData } -Start;
}
Use-Icinga -LibOnly -Daemon;
function Add-IcingaServiceCheckDaemon()
{
param (
$IcingaDaemonData
);
$IcingaDaemonData.IcingaThreadPool.Add('ServiceCheckPool', (New-IcingaThreadPool -MaxInstances (Get-IcingaConfigTreeCount -Path 'BackgroundDaemon.RegisteredServices')));
Use-Icinga -LibOnly -Daemon;
while ($TRUE) {
$IcingaDaemonData.IcingaThreadPool.Add('ServiceCheckPool', (New-IcingaThreadPool -MaxInstances (Get-IcingaConfigTreeCount -Path 'BackgroundDaemon.RegisteredServices')));
$RegisteredServices = Get-IcingaRegisteredServiceChecks;
while ($TRUE) {
foreach ($service in $RegisteredServices.Keys) {
[string]$ThreadName = [string]::Format('Icinga_Background_Service_Check_{0}', $service);
if ((Test-IcingaThread $ThreadName)) {
continue;
}
$RegisteredServices = Get-IcingaRegisteredServiceChecks;
[hashtable]$ServiceArgs = @{ };
if ($null -ne $RegisteredServices[$service].Arguments) {
foreach ($property in $RegisteredServices[$service].Arguments.PSObject.Properties) {
if ($ServiceArgs.ContainsKey($property.Name)) {
continue;
}
$ServiceArgs.Add($property.Name, $property.Value)
}
}
Start-IcingaServiceCheckTask -CheckId $service -CheckCommand $RegisteredServices[$service].CheckCommand -Arguments $ServiceArgs -Interval $RegisteredServices[$service].Interval -TimeIndexes $RegisteredServices[$service].TimeIndexes;
foreach ($service in $RegisteredServices.Keys) {
[string]$ThreadName = [string]::Format('Icinga_Background_Service_Check_{0}', $service);
if ((Test-IcingaThread $ThreadName)) {
continue;
}
Start-Sleep -Seconds 1;
}
};
New-IcingaThreadInstance -Name "Icinga_PowerShell_ServiceCheck_Scheduler" -ThreadPool $IcingaDaemonData.IcingaThreadPool.BackgroundPool -ScriptBlock $ScriptBlock -Arguments @( $global:IcingaDaemonData ) -Start;
[hashtable]$ServiceArgs = @{ };
if ($null -ne $RegisteredServices[$service].Arguments) {
foreach ($property in $RegisteredServices[$service].Arguments.PSObject.Properties) {
if ($ServiceArgs.ContainsKey($property.Name)) {
continue;
}
$ServiceArgs.Add($property.Name, $property.Value)
}
}
Start-IcingaServiceCheckTask -CheckId $service -CheckCommand $RegisteredServices[$service].CheckCommand -Arguments $ServiceArgs -Interval $RegisteredServices[$service].Interval -TimeIndexes $RegisteredServices[$service].TimeIndexes;
}
Start-Sleep -Seconds 1;
}
}
function Start-IcingaServiceCheckTask()
@ -72,161 +75,12 @@ function Start-IcingaServiceCheckTask()
[string]$ThreadName = [string]::Format('Icinga_Background_Service_Check_{0}', $CheckId);
$ScriptBlock = {
param($IcingaDaemonData, $CheckCommand, $Arguments, $Interval, $TimeIndexes, $CheckId);
Use-Icinga -LibOnly -Daemon;
$PassedTime = 0;
$SortedResult = $null;
$PerfCache = @{ };
$AverageCalc = @{ };
[int]$MaxTime = 0;
# Initialise some global variables we use to actually store check result data from
# plugins properly. This is doable from each thread instance as this part isn't
# shared between daemons
New-IcingaCheckSchedulerEnvironment;
foreach ($index in $TimeIndexes) {
# Only allow numeric index values
if ((Test-Numeric $index) -eq $FALSE) {
continue;
}
if ($AverageCalc.ContainsKey([string]$index) -eq $FALSE) {
$AverageCalc.Add(
[string]$index,
@{
'Interval' = ([int]$index);
'Time' = ([int]$index * 60);
'Sum' = 0;
'Count' = 0;
}
);
}
if ($MaxTime -le [int]$index) {
$MaxTime = [int]$index;
}
}
[int]$MaxTimeInSeconds = $MaxTime * 60;
if (-Not ($global:Icinga.CheckData.ContainsKey($CheckCommand))) {
$global:Icinga.CheckData.Add($CheckCommand, @{ });
$global:Icinga.CheckData[$CheckCommand].Add('results', @{ });
$global:Icinga.CheckData[$CheckCommand].Add('average', @{ });
}
$LoadedCacheData = Get-IcingaCacheData -Space 'sc_daemon' -CacheStore 'checkresult_store' -KeyName $CheckCommand;
if ($null -ne $LoadedCacheData) {
foreach ($entry in $LoadedCacheData.PSObject.Properties) {
$global:Icinga.CheckData[$CheckCommand]['results'].Add(
$entry.name,
@{ }
);
foreach ($item in $entry.Value.PSObject.Properties) {
$global:Icinga.CheckData[$CheckCommand]['results'][$entry.name].Add(
$item.Name,
$item.Value
);
}
}
}
while ($TRUE) {
if ($PassedTime -ge $Interval) {
try {
& $CheckCommand @Arguments | Out-Null;
} catch {
# Just for debugging. Not required in production or usable at all
$ErrMsg = $_.Exception.Message;
Write-IcingaConsoleError $ErrMsg;
}
try {
$UnixTime = Get-IcingaUnixTime;
foreach ($result in $global:Icinga.CheckData[$CheckCommand]['results'].Keys) {
[string]$HashIndex = $result;
$SortedResult = $global:Icinga.CheckData[$CheckCommand]['results'][$HashIndex].GetEnumerator() | Sort-Object name -Descending;
Add-IcingaHashtableItem -Hashtable $PerfCache -Key $HashIndex -Value @{ } | Out-Null;
foreach ($timeEntry in $SortedResult) {
if ((Test-Numeric $timeEntry.Value) -eq $FALSE) {
continue;
}
foreach ($calc in $AverageCalc.Keys) {
if (($UnixTime - $AverageCalc[$calc].Time) -le [int]$timeEntry.Key) {
$AverageCalc[$calc].Sum += $timeEntry.Value;
$AverageCalc[$calc].Count += 1;
}
}
if (($UnixTime - $MaxTimeInSeconds) -le [int]$timeEntry.Key) {
Add-IcingaHashtableItem -Hashtable $PerfCache[$HashIndex] -Key ([string]$timeEntry.Key) -Value ([string]$timeEntry.Value) | Out-Null;
}
}
foreach ($calc in $AverageCalc.Keys) {
if ($AverageCalc[$calc].Count -ne 0) {
$AverageValue = ($AverageCalc[$calc].Sum / $AverageCalc[$calc].Count);
[string]$MetricName = Format-IcingaPerfDataLabel (
[string]::Format('{0}_{1}', $HashIndex, $AverageCalc[$calc].Interval)
);
Add-IcingaHashtableItem `
-Hashtable $global:Icinga.CheckData[$CheckCommand]['average'] `
-Key $MetricName -Value $AverageValue -Override | Out-Null;
}
$AverageCalc[$calc].Sum = 0;
$AverageCalc[$calc].Count = 0;
}
}
# Flush data we no longer require in our cache to free memory
[array]$CheckStores = $global:Icinga.CheckData[$CheckCommand]['results'].Keys;
foreach ($CheckStore in $CheckStores) {
[string]$CheckKey = $CheckStore;
[array]$CheckTimeStamps = $global:Icinga.CheckData[$CheckCommand]['results'][$CheckKey].Keys;
foreach ($TimeSample in $CheckTimeStamps) {
if (($UnixTime - $MaxTimeInSeconds) -gt [int]$TimeSample) {
Remove-IcingaHashtableItem -Hashtable $global:Icinga.CheckData[$CheckCommand]['results'][$CheckKey] -Key ([string]$TimeSample);
}
}
}
Set-IcingaCacheData -Space 'sc_daemon' -CacheStore 'checkresult' -KeyName $CheckCommand -Value $global:Icinga.CheckData[$CheckCommand]['average'];
# Write collected metrics to disk in case we reload the daemon. We will load them back into the module after reload then
Set-IcingaCacheData -Space 'sc_daemon' -CacheStore 'checkresult_store' -KeyName $CheckCommand -Value $PerfCache;
} catch {
# Just for debugging. Not required in production or usable at all
$ErrMsg = $_.Exception.Message;
Write-IcingaConsoleError 'Failed to handle check result processing: {0}' -Objects $ErrMsg;
}
# Cleanup the error stack and remove not required data
$Error.Clear();
# Always ensure our check data is cleared regardless of possible
# exceptions which might occur
Get-IcingaCheckSchedulerPerfData | Out-Null;
Get-IcingaCheckSchedulerPluginOutput | Out-Null;
$PassedTime = 0;
$SortedResult.Clear();
$PerfCache.Clear();
}
$PassedTime += 1;
Start-Sleep -Seconds 1;
# Force PowerShell to call the garbage collector to free memory
[System.GC]::Collect();
}
};
New-IcingaThreadInstance -Name $ThreadName -ThreadPool $IcingaDaemonData.IcingaThreadPool.ServiceCheckPool -ScriptBlock $ScriptBlock -Arguments @( $global:IcingaDaemonData, $CheckCommand, $Arguments, $Interval, $TimeIndexes, $CheckId ) -Start;
New-IcingaThreadInstance -Name $ThreadName -ThreadPool $IcingaDaemonData.IcingaThreadPool.ServiceCheckPool -Command 'Add-IcingaServiceCheckTask' -CmdParameters @{
'IcingaDaemonData' = $global:IcingaDaemonData;
'CheckCommand' = $CheckCommand;
'Arguments' = $Arguments;
'Interval' = $Interval;
'TimeIndexes' = $TimeIndexes
'CheckId' = $CheckId;
} -Start;
}

View file

@ -25,7 +25,7 @@ function Exit-IcingaThrowCritical()
Set-IcingaInternalPluginExitCode -ExitCode $IcingaEnums.IcingaExitCode.Critical;
Set-IcingaInternalPluginException -PluginException $OutputMessage;
if ($null -eq $global:IcingaDaemonData -Or $global:IcingaDaemonData.FrameworkRunningAsDaemon -eq $FALSE) {
if ($null -eq $global:IcingaDaemonData -Or ($global:IcingaDaemonData.FrameworkRunningAsDaemon -eq $FALSE -And $global:IcingaDaemonData.JEAContext -eq $FALSE)) {
Write-IcingaConsolePlain $OutputMessage;
exit $IcingaEnums.IcingaExitCode.Critical;
}

View file

@ -110,7 +110,7 @@ function Exit-IcingaThrowException()
Set-IcingaInternalPluginExitCode -ExitCode $IcingaEnums.IcingaExitCode.Unknown;
Set-IcingaInternalPluginException -PluginException $OutputMessage;
if ($null -eq $global:IcingaDaemonData -Or $global:IcingaDaemonData.FrameworkRunningAsDaemon -eq $FALSE) {
if ($null -eq $global:IcingaDaemonData -Or ($global:IcingaDaemonData.FrameworkRunningAsDaemon -eq $FALSE -And $global:IcingaDaemonData.JEAContext -eq $FALSE)) {
Write-IcingaConsolePlain $OutputMessage;
exit $IcingaEnums.IcingaExitCode.Unknown;
}

View file

@ -4,17 +4,52 @@ function Exit-IcingaExecutePlugin()
[string]$Command = ''
);
$JEAProfile = Get-IcingaJEAContext;
Invoke-IcingaInternalServiceCall -Command $Command -Arguments $args;
try {
# Load the entire framework now, as we require to execute plugins locally
if ($null -eq $global:IcingaDaemonData) {
Use-Icinga;
}
Exit-IcingaPluginNotInstalled -Command $Command;
exit (& $Command @args);
if ([string]::IsNullOrEmpty($JEAProfile) -eq $FALSE) {
$ErrorHandler = ''
$JEARun = (
& powershell.exe -ConfigurationName $JEAProfile -NoLogo -NoProfile -Command {
Use-Icinga;
$global:IcingaDaemonData.JEAContext = $TRUE;
$Command = $args[0];
$Arguments = $args[1];
$Output = '';
try {
$ExitCode = (& $Command @Arguments);
$Output = (Get-IcingaInternalPluginOutput);
$ExitCode = (Get-IcingaInternalPluginExitCode);
} catch {
$Output = [string]::Format('[UNKNOWN] Icinga Exception: Error while executing plugin in JEA context{0}{0}{1}', (New-IcingaNewLine), $_.Exception.Message);
$ExitCode = 3;
}
return @{
'Output' = $Output;
'PerfData' = (Get-IcingaCheckSchedulerPerfData)
'ExitCode' = $ExitCode;
}
} -args $Command, $args
) 2>$ErrorHandler;
if ($LASTEXITCODE -ge 0) {
Write-IcingaPluginResult -PluginOutput $JEARun.Output -PluginPerfData $JEARun.PerfData;
exit $JEARun.ExitCode;
} else {
Write-IcingaConsolePlain '[UNKNOWN] Icinga Exception: Unable to start the PowerShell.exe with the provided JEA profile "{0}" for CheckCommand: {1}' -Objects $JEAProfile, $Command;
exit 3;
}
} else {
exit (& $Command @args);
}
} catch {
$ExMsg = $_.Exception.Message;
$StackTrace = $_.ScriptStackTrace;

View file

@ -0,0 +1,4 @@
function Get-IcingaInternalPluginExitCode()
{
return $Global:Icinga.PluginExecution.LastExitCode;
}

View file

@ -0,0 +1,8 @@
function Get-IcingaInternalPluginOutput()
{
if ([string]::IsNullOrEmpty($Global:Icinga.PluginExecution.PluginException) -eq $FALSE) {
return $Global:Icinga.PluginExecution.PluginException;
}
return $Global:Icinga.CheckResults;
}

View file

@ -4,7 +4,7 @@ function Write-IcingaPluginOutput()
$Output
);
if ($global:IcingaDaemonData.FrameworkRunningAsDaemon -eq $FALSE) {
if ($global:IcingaDaemonData.FrameworkRunningAsDaemon -eq $FALSE -And $global:IcingaDaemonData.JEAContext -eq $FALSE) {
if ($null -ne $global:Icinga -And $global:Icinga.Minimal) {
Clear-Host;
}

View file

@ -21,7 +21,7 @@ function Write-IcingaPluginPerfData()
$CheckResultCache = $Global:Icinga.ThresholdCache[$CheckCommand];
if ($global:IcingaDaemonData.FrameworkRunningAsDaemon -eq $FALSE) {
if ($global:IcingaDaemonData.FrameworkRunningAsDaemon -eq $FALSE -And $global:IcingaDaemonData.JEAContext -eq $FALSE) {
[string]$PerfDataOutput = (Get-IcingaPluginPerfDataContent -PerfData $PerformanceData -CheckResultCache $CheckResultCache -IcingaCheck $IcingaCheck);
Write-IcingaConsolePlain ([string]::Format('| {0}', $PerfDataOutput));
} else {

View file

@ -0,0 +1,18 @@
function Write-IcingaPluginResult()
{
param (
[string]$PluginOutput = '',
[array]$PluginPerfData = @()
);
[string]$CheckResult = $PluginOutput;
if ($PluginPerfData -ne 0) {
$CheckResult += "`n`r| ";
foreach ($PerfData in $PluginPerfData) {
$CheckResult += $PerfData;
}
}
Write-IcingaConsolePlain $CheckResult;
}

View file

@ -0,0 +1,11 @@
function Get-IcingaForWindowsCertificate()
{
[string]$CertificateFolder = Join-Path -Path (Get-IcingaFrameworkRootPath) -ChildPath 'certificate';
[string]$CertificateFile = Join-Path -Path $CertificateFolder -ChildPath 'icingaforwindows.pfx';
if (-Not (Test-Path $CertificateFile)) {
return $null;
}
return ([Security.Cryptography.X509Certificates.X509Certificate2]::CreateFromCertFile($CertificateFile));
}

View file

@ -0,0 +1,59 @@
function Install-IcingaForWindowsCertificate()
{
param (
[string]$CertFile = '',
[string]$CertThumbprint = ''
);
[Security.Cryptography.X509Certificates.X509Certificate2]$Certificate = $null;
[string]$CertificateFolder = Join-Path -Path (Get-IcingaFrameworkRootPath) -ChildPath 'certificate';
[string]$CertificateFile = Join-Path -Path $CertificateFolder -ChildPath 'icingaforwindows.pfx';
[bool]$FoundCertificate = $FALSE;
if (-Not (Test-Path $CertificateFolder)) {
New-Item -ItemType Directory -Path $CertificateFolder -Force | Out-Null;
}
if (-Not (Test-IcingaAcl -Directory $CertificateFolder)) {
Set-IcingaAcl -Directory $CertificateFolder;
}
if (Test-Path $CertificateFile) {
Remove-ItemSecure -Path $CertificateFile -Force | Out-Null;
}
if ([string]::IsNullOrEmpty($CertFile) -eq $FALSE) {
if ([IO.Path]::GetExtension($CertFile) -ne '.pfx') {
ConvertTo-IcingaX509Certificate -CertFile $CertFile -OutFile $CertificateFile -Force | Out-Null;
} else {
Copy-ItemSecure -Path $CertFile -Destination $CertificateFile -Force | Out-Null;
}
}
if ([string]::IsNullOrEmpty($CertThumbprint) -eq $FALSE) {
$Certificate = Get-ChildItem -Path 'cert:\*' -Include $CertThumbprint -Recurse
if ($null -ne $Certificate) {
Export-Certificate -Cert $Certificate -FilePath $CertificateFile | Out-Null;
}
}
if ([string]::IsNullOrEmpty($CertFile) -And [string]::IsNullOrEmpty($CertThumbprint)) {
$IcingaHostCertificate = Get-IcingaAgentHostCertificate;
if ([string]::IsNullOrEmpty($IcingaHostCertificate.CertFile) -eq $FALSE) {
$LocalCert = ConvertTo-IcingaX509Certificate -CertFile $IcingaHostCertificate.CertFile -OutFile $CertificateFile -Force;
Import-PfxCertificate -FilePath $CertificateFile -CertStoreLocation 'Cert:\LocalMachine\My\' -Exportable | Out-Null;
Remove-ItemSecure -Path $CertificateFile -Force | Out-Null;
$Certificate = Get-ChildItem -Path 'cert:\*' -Include $LocalCert.Thumbprint -Recurse
Export-Certificate -Cert $Certificate -FilePath $CertificateFile | Out-Null;
}
}
if (Test-Path $CertificateFile) {
Write-IcingaConsoleNotice -Message 'Successfully installed Icinga for Windows certificate at "{0}"' -Objects $CertificateFile;
} else {
Write-IcingaConsoleError -Message 'Unable to install Icinga for Windows certificate, as with specified arguments and auto-lookup for Icinga Agent certificate, no certificate could be created' -Objects $CertificateFile;
}
}

Some files were not shown because too many files have changed in this diff Show more