Skip to content

Commit 97aacfe

Browse files
committed
Adds support to authenticate with the Icinga for Windows certificate for the REST-Api
1 parent 9c69c35 commit 97aacfe

File tree

9 files changed

+168
-6
lines changed

9 files changed

+168
-6
lines changed

doc/100-General/10-Changelog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ Released closed milestones can be found on [GitHub](https://github.com/Icinga/ic
1919
### Enhancements
2020

2121
* [#732](https://github.com/Icinga/icinga-powershell-framework/pull/732) Adds support for TLS 1.3 and improves startup response
22+
* [#740](https://github.com/Icinga/icinga-powershell-framework/pull/740) Adds support to use the Icinga for Windows certificate to authenticate for the Icinga for Windows API, allowing smoother transitions by not disabling certificate validation and adds new command `Invoke-IcingaForWindowsRESTApi` for easier API communication
2223

2324
## 1.12.3 (2024-04-24)
2425

jobs/RenewCertificate.ps1

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,28 @@ if ($RegisteredBackgroundDaemons.ContainsKey('Start-IcingaWindowsRESTApi')) {
2222
}
2323
}
2424

25-
Install-IcingaForWindowsCertificate -CertFile $CertificatePath;
25+
# Wait during the initial run as long as the certificate is not available
26+
while ($TRUE) {
27+
Install-IcingaForWindowsCertificate -CertFile $CertificatePath;
28+
29+
if ((Test-IcingaForWindowsCertificate) -eq $FALSE) {
30+
Write-IcingaEventMessage -EventId 1508 -Namespace 'Framework';
31+
Start-Sleep -Seconds 60;
32+
33+
continue;
34+
}
35+
36+
break;
37+
}
38+
39+
# Ensure we import the Icinga ca.crt to the root store, which allows us to use the certificate
40+
# of the agent to connect the the Icinga for Windows API without having to break the certificate trust
41+
[bool]$CAImportSuccess = Import-IcingaCAToAuthRoot;
42+
43+
if ($CAImportSuccess -eq $FALSE) {
44+
Write-IcingaEventMessage -EventId 1509 -Namespace 'Framework';
45+
exit 1;
46+
}
2647

2748
# Tell the Task-Scheduler that the script was executed fine
2849
exit 0;

lib/core/framework/Invoke-IcingaForWindowsMigration.psm1

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,4 +143,13 @@ function Invoke-IcingaForWindowsMigration()
143143

144144
Set-IcingaForWindowsMigration -MigrationVersion (New-IcingaVersionObject -Version '1.12.3');
145145
}
146+
147+
if (Test-IcingaForWindowsMigration -MigrationVersion (New-IcingaVersionObject -Version '1.13.0')) {
148+
Write-IcingaConsoleNotice 'Applying pending migrations required for Icinga for Windows v1.13.0';
149+
150+
# Updates certificate renew task to handle changes made which now stores the Icinga CA inside the cert store
151+
Start-IcingaWindowsScheduledTaskRenewCertificate;
152+
153+
Set-IcingaForWindowsMigration -MigrationVersion (New-IcingaVersionObject -Version '1.13.0');
154+
}
146155
}

lib/core/icingaagent/tests/Test-IcingaForWindows.psm1

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ function Test-IcingaForWindows()
146146
if ($ResolveProblems) {
147147
Write-IcingaConsoleNotice 'Fixing problems with missing not signed Icinga for Windows certificate by re-creating it from the Icinga Agent certificate';
148148
Start-IcingaWindowsScheduledTaskRenewCertificate;
149+
Start-Sleep -Seconds 5;
149150
$IfWCertificate = Get-IcingaForWindowsCertificate;
150151

151152
if ($IfWCertificate.Issuer.ToLower() -eq ([string]::Format('cn={0}', $Hostname).ToLower())) {
@@ -188,17 +189,16 @@ function Test-IcingaForWindows()
188189
}
189190

190191
Set-IcingaTLSVersion;
191-
Enable-IcingaUntrustedCertificateValidation -SuppressMessages;
192192

193193
try {
194-
$ApiResult = Invoke-WebRequest -UseBasicParsing -Uri 'https://localhost:5668/v1' -ErrorAction Stop;
195-
Write-IcingaTestOutput -Severity Passed -Message 'The Icinga for Windows REST-Api responded successfully on "https://localhost:5668/v1"';
194+
$ApiResult = Invoke-IcingaForWindowsRESTApi;
195+
Write-IcingaTestOutput -Severity Passed -Message 'The Icinga for Windows REST-Api responded successfully on this machine';
196196
} catch {
197197
if ($IfWService.User.ToLower() -eq 'nt authority\networkservice') {
198-
Write-IcingaTestOutput -Severity Failed -Message ([string]::Format('The Icinga for Windows REST-Api responded with an error on "https://localhost:5668/v1", which is expected when using the default NetworkService account [IWKB000018]: "{0}"', $_.Exception.Message));
198+
Write-IcingaTestOutput -Severity Failed -Message ([string]::Format('The Icinga for Windows REST-Api responded with an error on this machine, which is expected when using the default NetworkService account [IWKB000018]: "{0}"', $_.Exception.Message));
199199
} else {
200200
if ($ResolveProblems -eq $FALSE) {
201-
Write-IcingaTestOutput -Severity Failed -Message ([string]::Format('The Icinga for Windows REST-Api responded with an error on "https://localhost:5668/v1": "{0}"', $_.Exception.Message));
201+
Write-IcingaTestOutput -Severity Failed -Message ([string]::Format('The Icinga for Windows REST-Api responded with an error on this machine: "{0}"', $_.Exception.Message));
202202
} else {
203203
Write-IcingaConsoleNotice 'Fixing problems with Icinga for Windows REST-Api by restarting the Icinga for Windows service';
204204
Restart-IcingaForWindows;

lib/core/logging/Icinga_EventLog_Enums.psm1

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,18 @@ if ($null -eq $IcingaEventLogEnums -Or $IcingaEventLogEnums.ContainsKey('Framewo
122122
'Details' = 'One of the internal Icinga for Windows threads was being active but not responding for at least 5 minutes. The frozen thread has been terminated and restarted.';
123123
'EventId' = 1507;
124124
};
125+
1508 = @{
126+
'EntryType' = 'Warning';
127+
'Message' = 'Icinga for Windows certificate is pending for signing';
128+
'Details' = 'The Icinga for Windows certificate is pending to be signed. This happens if the Icinga Agent certificate is not yet signed by the Icinga CA. The creation will pause for some minutes and try again.';
129+
'EventId' = 1508;
130+
};
131+
1509 = @{
132+
'EntryType' = 'Error';
133+
'Message' = 'Icinga CA certificate could not be imported to the AuthRoot store';
134+
'Details' = 'The Icinga CA certificate could not be imported to the LocalMachine\AuthRoot store on this device. This will cause all local PowerShell requests to communicate with the API to be untrusted, requring to enable untrusted certificate request as otherwise the communication will fail.';
135+
'EventId' = 1509;
136+
};
125137
1550 = @{
126138
'EntryType' = 'Error';
127139
'Message' = 'Unsupported web authentication used';
@@ -164,6 +176,12 @@ if ($null -eq $IcingaEventLogEnums -Or $IcingaEventLogEnums.ContainsKey('Framewo
164176
'Details' = 'An exception occurred while executing Icinga for Windows code inside a JEA context.';
165177
'EventId' = 1600;
166178
};
179+
1700 = @{
180+
'EntryType' = 'Error';
181+
'Message' = 'Icinga Agent hostname is not set inside the constants.conf';
182+
'Details' = 'Your current Icinga Agent configuration is missing a crutial configuration part inside your constants.conf, which should set the current configured hostname as NodeName. As long as this configuration is missing, certain Icinga for Windows feature might only partially work. Re-Installing Icinga for Windows might resolve this issue.';
183+
'EventId' = 1700;
184+
};
167185
}
168186
};
169187
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
function Import-IcingaCAToAuthRoot()
2+
{
3+
$IcingaCAFile = Join-Path -Path $Env:ProgramData -ChildPath 'icinga2\var\lib\icinga2\certs\ca.crt';
4+
5+
if ((Test-Path $IcingaCAFile) -eq $FALSE) {
6+
return $FALSE;
7+
}
8+
9+
if (Test-IcingaCAInstalledToAuthRoot) {
10+
return $TRUE;
11+
}
12+
13+
Import-Certificate -FilePath $IcingaCAFile -CertStoreLocation 'Cert:\LocalMachine\AuthRoot\' | Out-Null;
14+
15+
return (
16+
Test-IcingaCAInstalledToAuthRoot
17+
);
18+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
function Invoke-IcingaForWindowsRESTApi()
2+
{
3+
param (
4+
[string]$Uri = 'v1',
5+
[ValidateSet('GET', 'POST')]
6+
[string]$Method = 'GET',
7+
[hashtable]$Body = @{ }
8+
);
9+
10+
if ((Test-IcingaCAInstalledToAuthRoot) -eq $FALSE) {
11+
Write-IcingaConsoleError 'The Icinga CA certificate is not installed to the local machine certificate store. Please run the "Start-IcingaWindowsScheduledTaskRenewCertificate" command to fix this issue.';
12+
return $null;
13+
}
14+
15+
Set-IcingaTLSVersion;
16+
$IcingaForWindowsCertificate = Get-IcingaForWindowsCertificate;
17+
$RestApiPort = 5668;
18+
[int]$Timeout = 20;
19+
$BackgroundDaemons = Get-IcingaBackgroundDaemons;
20+
21+
if ($null -ne $BackgroundDaemons -And $BackgroundDaemons.ContainsKey('Start-IcingaWindowsRESTApi')) {
22+
$Daemon = $BackgroundDaemons['Start-IcingaWindowsRESTApi'];
23+
24+
# Fetch our deamon configuration
25+
if ($Daemon.ContainsKey('-Port')) {
26+
$RestApiPort = $Daemon['-Port'];
27+
} elseif ($Daemon.ContainsKey('Port')) {
28+
$RestApiPort = $Daemon['Port'];
29+
}
30+
}
31+
32+
[string]$WebBaseURL = [string]::Format('https://{0}:{1}', ($IcingaForWindowsCertificate.Subject.Replace('CN=', ''), $RestApiPort));
33+
34+
$WebBaseURL = Join-WebPath -Path $WebBaseURL -ChildPath $Uri;
35+
36+
[hashtable]$Arguments = @{
37+
'-Method' = $Method;
38+
'-UseBasicParsing' = $TRUE;
39+
'-Uri' = $WebBaseURL;
40+
'-ContentType' = 'application/json';
41+
'-TimeoutSec' = $Timeout;
42+
'-Certificate' = $IcingaForWindowsCertificate;
43+
'-ErrorAction' = 'Stop';
44+
};
45+
46+
if ($null -ne $Body -And $Body.Count -ne 0 -And $Method -eq 'POST') {
47+
$Arguments.Add('-Body', (ConvertTo-JsonUTF8Bytes -InputObject $Body -Depth 100 -Compress));
48+
}
49+
50+
return (
51+
Invoke-WebRequest @Arguments
52+
);
53+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
function Test-IcingaCAInstalledToAuthRoot()
2+
{
3+
$IcingaCAFile = Join-Path -Path $Env:ProgramData -ChildPath 'icinga2\var\lib\icinga2\certs\ca.crt';
4+
5+
if ((Test-Path $IcingaCAFile) -eq $FALSE) {
6+
return $FALSE;
7+
}
8+
9+
$IcingaCACert = New-Object Security.Cryptography.X509Certificates.X509Certificate2 $IcingaCAFile;
10+
11+
[bool]$IcingaCAInstalled = $FALSE;
12+
13+
Get-ChildItem -Recurse -Path 'Cert:\LocalMachine\AuthRoot\' | Where-Object {
14+
if ($_.Thumbprint -eq $IcingaCACert.Thumbprint) {
15+
$IcingaCAInstalled = $TRUE;
16+
}
17+
};
18+
19+
$IcingaCACert = $null;
20+
21+
return $IcingaCAInstalled;
22+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
function Test-IcingaForWindowsCertificate()
2+
{
3+
$IfWCertificate = Get-IcingaForWindowsCertificate;
4+
$Hostname = Get-IcingaHostname -ReadConstants;
5+
6+
if ([string]::IsNullOrEmpty($Hostname)) {
7+
Write-IcingaEventMessage -EventId 1700 -Namespace 'Framework';
8+
return $FALSE;
9+
}
10+
11+
if ($null -eq $IfWCertificate) {
12+
return $FALSE;
13+
}
14+
15+
if ($IfWCertificate.Issuer.ToLower() -eq ([string]::Format('cn={0}', $Hostname).ToLower())) {
16+
return $FALSE;
17+
}
18+
19+
return $TRUE;
20+
}

0 commit comments

Comments
 (0)