diff --git a/doc/100-General/10-Changelog.md b/doc/100-General/10-Changelog.md index dcc20ffe..c628fe83 100644 --- a/doc/100-General/10-Changelog.md +++ b/doc/100-General/10-Changelog.md @@ -22,6 +22,7 @@ Released closed milestones can be found on [GitHub](https://github.com/Icinga/ic * [#679](https://github.com/Icinga/icinga-powershell-framework/pull/679) Adds a new data provider for fetching process information of Windows systems, while sorting all objects based on a process name and their process id * [#688](https://github.com/Icinga/icinga-powershell-framework/pull/688) Adds new handling to add scheduled tasks in Windows for interacting with Icinga for Windows core functionality as well as an auto renewal task for the Icinga for Windows certificate generation +* [#690](https://github.com/Icinga/icinga-powershell-framework/pull/690) Adds automatic renewal of the `icingaforwindows.pfx` certificate for the REST-Api daemon in case the certificate is not yet present, valid or changed during the runtime of the daemon while also making the `icingaforwindows.pfx` mandatory for all installations, regardless of JEA being used or not ## 1.11.2 (tbd) diff --git a/lib/core/framework/Invoke-IcingaForWindowsMigration.psm1 b/lib/core/framework/Invoke-IcingaForWindowsMigration.psm1 index 72896cb9..3409d8dd 100644 --- a/lib/core/framework/Invoke-IcingaForWindowsMigration.psm1 +++ b/lib/core/framework/Invoke-IcingaForWindowsMigration.psm1 @@ -95,6 +95,8 @@ function Invoke-IcingaForWindowsMigration() # Add a new scheduled task to automatically renew the Icinga for Windows certificate Register-IcingaWindowsScheduledTaskRenewCertificate -Force; + # Start the task to ensure the certificate is generated + Start-IcingaWindowsScheduledTaskRenewCertificate; Set-IcingaForWindowsMigration -MigrationVersion (New-IcingaVersionObject -Version '1.12.0'); } diff --git a/lib/core/installer/Start-IcingaForWindowsInstallation.psm1 b/lib/core/installer/Start-IcingaForWindowsInstallation.psm1 index 0463eaa5..68594103 100644 --- a/lib/core/installer/Start-IcingaForWindowsInstallation.psm1 +++ b/lib/core/installer/Start-IcingaForWindowsInstallation.psm1 @@ -312,11 +312,9 @@ function Start-IcingaForWindowsInstallation() }; } - # Install Icinga for Windows certificate if both, JEA and REST is installed - if ($InstallJEA -And $InstallRESTApi) { - Install-IcingaForWindowsCertificate; - Restart-IcingaWindowsService; - } + # Always install the Icinga for Windows certificate + Install-IcingaForWindowsCertificate; + Restart-IcingaWindowsService; # Update configuration and clear swap $ConfigSwap = Get-IcingaPowerShellConfig -Path 'Framework.Config.Swap'; diff --git a/lib/core/installer/menu/manage/framework/ToogleFrameworkApiChecks.psm1 b/lib/core/installer/menu/manage/framework/ToogleFrameworkApiChecks.psm1 index c54280f2..3dae83b8 100644 --- a/lib/core/installer/menu/manage/framework/ToogleFrameworkApiChecks.psm1 +++ b/lib/core/installer/menu/manage/framework/ToogleFrameworkApiChecks.psm1 @@ -7,10 +7,8 @@ function Invoke-IcingaForWindowsManagementConsoleToggleFrameworkApiChecks() Register-IcingaBackgroundDaemon -Command 'Start-IcingaWindowsRESTApi'; Add-IcingaRESTApiCommand -Command 'Invoke-IcingaCheck*' -Endpoint 'apichecks'; } - if ([string]::IsNullOrEmpty((Get-IcingaJEAContext)) -eq $FALSE) { - Install-IcingaForWindowsCertificate; - } + Install-IcingaForWindowsCertificate; Enable-IcingaFrameworkApiChecks; } diff --git a/lib/core/jea/Install-IcingaJeaProfile.psm1 b/lib/core/jea/Install-IcingaJeaProfile.psm1 index dcbd8a13..80eb5305 100644 --- a/lib/core/jea/Install-IcingaJeaProfile.psm1 +++ b/lib/core/jea/Install-IcingaJeaProfile.psm1 @@ -26,10 +26,7 @@ function Install-IcingaJEAProfile() Write-IcingaJEAProfile -RebuildFramework:$RebuildFramework -AllowScriptBlocks:$AllowScriptBlocks; Write-IcingaConsoleNotice 'Registering Icinga for Windows JEA profile' Register-IcingaJEAProfile -IcingaUser $IcingaUser -TestEnv:$TestEnv -ConstrainedLanguage:$ConstrainedLanguage; - - if ((Get-IcingaBackgroundDaemons).ContainsKey('Start-IcingaWindowsRESTApi')) { - Install-IcingaForWindowsCertificate; - } + Install-IcingaForWindowsCertificate; } Set-Alias -Name 'Update-IcingaJEAProfile' -Value 'Install-IcingaJEAProfile'; diff --git a/lib/core/wintasks/daemon/Register-TaskRenewCertificate.psm1 b/lib/core/wintasks/daemon/Register-TaskRenewCertificate.psm1 index 539d477c..c472b382 100644 --- a/lib/core/wintasks/daemon/Register-TaskRenewCertificate.psm1 +++ b/lib/core/wintasks/daemon/Register-TaskRenewCertificate.psm1 @@ -20,7 +20,7 @@ function Register-IcingaWindowsScheduledTaskRenewCertificate() $TaskPrincipal = New-ScheduledTaskPrincipal -GroupId 'S-1-5-32-544' -RunLevel 'Highest'; $TaskSettings = New-ScheduledTaskSettingsSet -DontStopIfGoingOnBatteries -AllowStartIfOnBatteries -StartWhenAvailable; - Register-ScheduledTask -TaskName $TaskName -TaskPath $TaskPath -Force -Principal $TaskPrincipal -Action $TaskAction -Trigger $TaskTrigger -Settings $TaskSettings; + Register-ScheduledTask -TaskName $TaskName -TaskPath $TaskPath -Force -Principal $TaskPrincipal -Action $TaskAction -Trigger $TaskTrigger -Settings $TaskSettings | Out-Null; - Write-IcingaConsoleWarning -Message 'The task "{0}" has been successfully registered at location "{1}".' -Objects $TaskName, $TaskPath; + Write-IcingaConsoleNotice -Message 'The task "{0}" has been successfully registered at location "{1}".' -Objects $TaskName, $TaskPath; } diff --git a/lib/daemon/Start-IcingaPowerShellDaemon.psm1 b/lib/daemon/Start-IcingaPowerShellDaemon.psm1 index b8ee4e0c..d5f12e11 100644 --- a/lib/daemon/Start-IcingaPowerShellDaemon.psm1 +++ b/lib/daemon/Start-IcingaPowerShellDaemon.psm1 @@ -39,7 +39,6 @@ function Start-IcingaForWindowsDaemon() Add-IcingaThreadPool -Name 'MainPool' -MaxInstances 20; $Global:Icinga.Public.Add('SSLCertificate', $Certificate); - New-IcingaThreadInstance -Name "Main" -ThreadPool (Get-IcingaThreadPool -Name 'MainPool') -Command 'Add-IcingaForWindowsDaemon' -Start; } else { Write-IcingaDebugMessage -Message 'Starting Icinga for Windows service inside JEA context' -Objects $RunAsService, $JEARestart, $JeaProfile; diff --git a/lib/daemons/RestAPI/daemon/New-IcingaForWindowsRESTApi.psm1 b/lib/daemons/RestAPI/daemon/New-IcingaForWindowsRESTApi.psm1 index 247f6d2a..dde51c8b 100644 --- a/lib/daemons/RestAPI/daemon/New-IcingaForWindowsRESTApi.psm1 +++ b/lib/daemons/RestAPI/daemon/New-IcingaForWindowsRESTApi.psm1 @@ -54,21 +54,20 @@ function New-IcingaForWindowsRESTApi() Write-IcingaDebugMessage -Message ($Global:Icinga.Public.Daemons.RESTApi.RegisteredEndpoints | Out-String); - if ($Global:Icinga.Protected.JEAContext) { - if ($Global:Icinga.Public.ContainsKey('SSLCertificate') -eq $FALSE -Or $null -eq $Global:Icinga.Public.SSLCertificate) { - Write-IcingaEventMessage -EventId 2001 -Namespace 'RESTApi'; - return; + while ($TRUE) { + if ($null -eq $Global:Icinga.Public.SSLCertificate) { + $Global:Icinga.Public.SSLCertificate = (Get-IcingaForWindowsCertificate); + } else { + break; } - $Certificate = $Global:Icinga.Public.SSLCertificate; - } else { - $Certificate = Get-IcingaSSLCertForSocket -CertFile $CertFile -CertThumbprint $CertThumbprint; + # Wait 5 minutes and try again + Write-IcingaEventMessage -EventId 2002 -Namespace 'RESTApi'; + Start-Sleep -Seconds (60 * 5); } - if ($null -eq $Certificate) { - Write-IcingaEventMessage -EventId 2000 -Namespace 'RESTApi'; - return; - } + # Create a background thread to renew the certificate on a regular basis + Start-IcingaForWindowsCertificateThreadTask; $Socket = New-IcingaTCPSocket -Address $Address -Port $Port -Start; @@ -82,7 +81,7 @@ function New-IcingaForWindowsRESTApi() $Connection = Open-IcingaTCPClientConnection ` -Client (New-IcingaTCPClient -Socket $Socket) ` - -Certificate $Certificate; + -Certificate $Global:Icinga.Public.SSLCertificate; if ($Connection.Client -eq $null -Or $Connection.Stream -eq $null) { Close-IcingaTCPConnection -Connection $Connection; diff --git a/lib/daemons/RestAPI/eventlog/Register-IcingaEventLogMessagesRESTApi.psm1 b/lib/daemons/RestAPI/eventlog/Register-IcingaEventLogMessagesRESTApi.psm1 index 3e77142b..7dc1744b 100644 --- a/lib/daemons/RestAPI/eventlog/Register-IcingaEventLogMessagesRESTApi.psm1 +++ b/lib/daemons/RestAPI/eventlog/Register-IcingaEventLogMessagesRESTApi.psm1 @@ -11,9 +11,27 @@ function Register-IcingaEventLogMessagesRESTApi() 2001 = @{ 'EntryType' = 'Error'; 'Message' = 'Failed to start REST-Api daemon in JEA context'; - 'Details' = 'Icinga for Windows is being used inside a JEA context as service with the REST-Api daemon. To establish a secure TLS socket, it is required to create certificates in advance for the socket to bind on with "Install-IcingaForWindowsCertificate". The REST-Api daemon will now exit.'; + 'Details' = 'Icinga for Windows is being used inside a JEA context as service with the REST-Api daemon. To establish a secure TLS socket, it is required to create certificates in advance for the socket to bind on with "Start-IcingaWindowsScheduledTaskRenewCertificate". The REST-Api daemon will now exit.'; 'EventId' = 2001; }; + 2002 = @{ + 'EntryType' = 'Warning'; + 'Message' = 'Icinga for Windows certificate not ready'; + 'Details' = 'The Icinga for Windows REST-Api was not able to fetch the icingaforwindows.pfx certificate file. You can manually enforce the certificate creation by using the command "Start-IcingaWindowsScheduledTaskRenewCertificate". Once successful, this message should disappear and the REST-Api start. If the error persist, ensure your Icinga Agent certificate is configured properly and signed by your Icinga CA. This check is queued every 5 minutes and should vanish once everything works fine.'; + 'EventId' = 2002; + }; + 2003 = @{ + 'EntryType' = 'Warning'; + 'Message' = 'Icinga for Windows certificate was not found'; + 'Details' = 'The Icinga for Windows "icingaforwindows.pfx" file was not found on the system while the REST-Api is running. Please ensure the certificate is created shortly, as the daemon will no longer work once it will be restarted or the certificate is due for renewal. Please run "Start-IcingaWindowsScheduledTaskRenewCertificate" to re-create the certificate on your machine.' + 'EventId' = 2003; + }; + 2004 = @{ + 'EntryType' = 'Information'; + 'Message' = 'Icinga for Windows certificate was renewed'; + 'Details' = 'The Icinga for Windows certificate has been modified and was updated inside the Icinga for Windows REST-Api daemon.' + 'EventId' = 2004; + }; 2050 = @{ 'EntryType' = 'Error'; 'Message' = 'Failed to parse received REST-Api call'; diff --git a/lib/daemons/RestAPI/threads/New-IcingaForWindowsCertificateThreadTaskInstance.psm1 b/lib/daemons/RestAPI/threads/New-IcingaForWindowsCertificateThreadTaskInstance.psm1 new file mode 100644 index 00000000..665995e7 --- /dev/null +++ b/lib/daemons/RestAPI/threads/New-IcingaForWindowsCertificateThreadTaskInstance.psm1 @@ -0,0 +1,23 @@ +function New-IcingaForWindowsCertificateThreadTaskInstance() +{ + $IcingaHostname = Get-IcingaHostname -ReadConstants; + + while ($TRUE) { + # Check every 10 minutes if our certificate is present and update it in case it is + # missing or updates have happened + $NewIcingaForWindowsCertificate = Get-IcingaForWindowsCertificate; + + if ($null -ne $NewIcingaForWindowsCertificate) { + if ($NewIcingaForWindowsCertificate.Issuer.ToLower() -eq ([string]::Format('cn={0}', $IcingaHostname).ToLower())) { + Write-IcingaEventMessage -EventId 1506 -Namespace 'Framework'; + } else { + if ($Global:Icinga.Public.SSLCertificate.GetCertHashString() -ne $NewIcingaForWindowsCertificate.GetCertHashString()) { + $Global:Icinga.Public.SSLCertificate = $NewIcingaForWindowsCertificate; + Write-IcingaEventMessage -EventId 2004 -Namespace 'RESTApi'; + } + } + } + + Start-Sleep -Seconds (60 * 10); + } +} diff --git a/lib/daemons/RestAPI/threads/Start-IcingaForWindowsCertificateThreadTask.psm1 b/lib/daemons/RestAPI/threads/Start-IcingaForWindowsCertificateThreadTask.psm1 new file mode 100644 index 00000000..bb8223cf --- /dev/null +++ b/lib/daemons/RestAPI/threads/Start-IcingaForWindowsCertificateThreadTask.psm1 @@ -0,0 +1,8 @@ +function Start-IcingaForWindowsCertificateThreadTask() +{ + New-IcingaThreadInstance ` + -Name 'CertificateRenewThread' ` + -ThreadPool (New-IcingaThreadPool -MaxInstances 1) ` + -Command 'New-IcingaForWindowsCertificateThreadTaskInstance' ` + -Start; +}