lib/core/icingaagent/installer/Install-IcingaAgentCertificates.psm1
<# .SYNOPSIS Installs the required certificates for the Icinga Agent including the entire signing process either by using the CA-Proxy, the CA-Server directly or by manually signing the request on the CA master .DESCRIPTION Installs the required certificates for the Icinga Agent including the entire signing process either by using the CA-Proxy, the CA-Server directly or by manually signing the request on the CA master .FUNCTIONALITY Creates, installs and signs required certificates for the Icinga Agent .EXAMPLE # Connect to the CA server with a ticket to fully complete the request PS>Install-IcingaAgentCertificates -Hostname 'windows.example.com' -Endpoint 'icinga2.example.com' -Ticket 'my_secret_ticket'; .EXAMPLE # Connect to the CA server without a ticket, to create the sign request on the master PS>Install-IcingaAgentCertificates -Hostname 'windows.example.com' -Endpoint 'icinga2.example.com'; .EXAMPLE # Uses the Icinga ca.crt from a local filesystem and prepares the Icinga Agent for receiving connections from the Master/Satellite for signing PS>Install-IcingaAgentCertificates -Hostname 'windows.example.com' -CACert 'C:\users\public\icinga2\ca.crt'; .EXAMPLE # Uses the Icinga ca.crt from a web resource and prepares the Icinga Agent for receiving connections from the Master/Satellite for signing PS>Install-IcingaAgentCertificates -Hostname 'windows.example.com' -CACert 'https://example.com/icinga2/ca.crt'; .PARAMETER Hostname The hostname of the local system. Has to match the object name within the Icinga configuration .PARAMETER Endpoint The address of either the Icinga CA master or a parent node of the Agent to transmit the request to the CA master .PARAMETER Port The port used for Icinga communication. Uses 5665 as default .PARAMETER CACert Allows to specify the path to the ca.crt from the Icinga CA master on a local, network or web share to allow certificate generation in case the Icinga Agent is not able to connect to it's parent hosts .PARAMETER Ticket The ticket number for the signing request which is either generated by Icinga 2 or the Icinga Director .PARAMETER Force Ignores existing certificates and will force the creation, overriding existing certificates .INPUTS System.String .OUTPUTS System.Boolean .LINK https://github.com/Icinga/icinga-powershell-framework #> function Install-IcingaAgentCertificates() { param( [string]$Hostname, [string]$Endpoint, [int]$Port = 5665, [string]$CACert, [string]$Ticket, [switch]$Force = $FALSE ); if ([string]::IsNullOrEmpty($Hostname)) { Write-IcingaConsoleError 'Failed to install Icinga Agent certificates. Please provide a hostname'; return $FALSE; } # Default for Icinga 2.8.0 and above [string]$NewCertificateDirectory = (Join-Path -Path $Env:ProgramData -ChildPath 'icinga2\var\lib\icinga2\certs\'); [string]$OldCertificateDirectory = (Join-Path -Path $Env:ProgramData -ChildPath 'icinga2\etc\icinga2\pki\'); [string]$CertificateDirectory = $NewCertificateDirectory; if ((Compare-IcingaVersions -RequiredVersion '2.8.0') -eq $FALSE) { # Certificate path for versions older than 2.8.0 $CertificateDirectory = $OldCertificateDirectory; Move-IcingaAgentCertificates -Source $NewCertificateDirectory -Destination $OldCertificateDirectory; } else { Move-IcingaAgentCertificates -Source $OldCertificateDirectory -Destination $NewCertificateDirectory; } if (-Not (Test-IcingaAgentCertificates -CertDirectory $CertificateDirectory -Hostname $Hostname -Force $Force)) { Write-IcingaConsoleNotice ([string]::Format('Generating host certificates for host "{0}"', $Hostname)); $arguments = [string]::Format('pki new-cert --cn {0} --key {1}{0}.key --cert {1}{0}.crt', $Hostname, $CertificateDirectory ); if ((Start-IcingaAgentCertificateProcess -Arguments $arguments) -eq $FALSE) { Write-IcingaConsoleError 'Failed to generate host certificate'; return $FALSE; } # Once we generated new host certificates, we always require to sign them if possible $Force = $TRUE; } if ([string]::IsNullOrEmpty($Endpoint) -And [string]::IsNullOrEmpty($CACert)) { Write-IcingaConsoleWarning 'Your host certificates were generated successfully. Please either specify an endpoint to connect to or provide the path to a valid ca.crt'; return $FALSE; } if (-Not [string]::IsNullOrEmpty($Endpoint)) { if (-Not (Test-IcingaAgentCertificates -CertDirectory $CertificateDirectory -Hostname $Hostname -TestTrustedParent -Force $Force)) { Write-IcingaConsoleNotice ([string]::Format('Fetching trusted master certificate from "{0}"', $Endpoint)); # Argument --key for save-cert is deprecated starting with Icinga 2.12.0 if (Compare-IcingaVersions -RequiredVersion '2.12.0') { $arguments = [string]::Format('pki save-cert --trustedcert {0}trusted-parent.crt --host {1} --port {2}', $CertificateDirectory, $Endpoint, $Port ); } else { $arguments = [string]::Format('pki save-cert --key {0}{1}.key --trustedcert {0}trusted-parent.crt --host {2} --port {3}', $CertificateDirectory, $Hostname, $Endpoint, $Port ); } if ((Start-IcingaAgentCertificateProcess -Arguments $arguments) -eq $FALSE) { Write-IcingaConsoleError -Message 'Unable to connect to your provided Icinga CA. Please verify the entered configuration is correct.' ` 'If you are not able to connect to your Icinga CA from this machine, you will have to provide the path' ` 'to your Icinga ca.crt and use the CA-Proxy certificate handling.'; return $FALSE; } } if (-Not (Test-IcingaAgentCertificates -CertDirectory $CertificateDirectory -Hostname $Hostname -TestCACert -Force $Force)) { [string]$PKIRequest = 'pki request --host {0} --port {1} --ticket {4} --key {2}{3}.key --cert {2}{3}.crt --trustedcert {2}trusted-parent.crt --ca {2}ca.crt'; if ([string]::IsNullOrEmpty($Ticket)) { $PKIRequest = 'pki request --host {0} --port {1} --key {2}{3}.key --cert {2}{3}.crt --trustedcert {2}trusted-parent.crt --ca {2}ca.crt'; } $arguments = [string]::Format($PKIRequest, $Endpoint, $Port, $CertificateDirectory, $Hostname, $Ticket ); if ((Start-IcingaAgentCertificateProcess -Arguments $arguments) -eq $FALSE) { Write-IcingaConsoleError 'Failed to sign Icinga certificate'; return $FALSE; } if ([string]::IsNullOrEmpty($Ticket)) { Write-IcingaConsoleNotice 'Your certificates were generated successfully. Please sign the certificate now on your Icinga CA master. You can lookup open requests with "icinga2 ca list"'; } else { Write-IcingaConsoleNotice 'Icinga certificates successfully installed'; } } return $TRUE; } elseif (-Not [string]::IsNullOrEmpty($CACert)) { if (-Not (Copy-IcingaAgentCACertificate -CAPath $CACert -Destination $CertificateDirectory)) { return $FALSE; } Write-IcingaConsoleNotice 'Host-Certificates and ca.crt are present. Please start your Icinga Agent now and manually sign your certificate request on your CA master. You can lookup open requests with "icinga2 ca list"'; } return $TRUE; } function Start-IcingaAgentCertificateProcess() { param( $Arguments ); $Binary = Get-IcingaAgentBinary; $Process = Start-IcingaProcess -Executable $Binary -Arguments $Arguments; if ($Process.ExitCode -ne 0) { Write-IcingaConsoleError ([string]::Format('Failed to create certificate.{0}Arguments: {1}{0}Error:{2} {3}', "`r`n", $Arguments, $Process.Message, $Process.Error)); return $FALSE; } Write-IcingaConsoleNotice $Process.Message; return $TRUE; } function Move-IcingaAgentCertificates() { param( [string]$Source, [string]$Destination ); $SourceDir = Join-Path -Path $Source -ChildPath '\*'; $TargetDir = Join-Path -Path $Destination -ChildPath '\'; Move-Item -Path $SourceDir -Destination $TargetDir; } function Test-IcingaAgentCertificates() { param( [string]$CertDirectory, [string]$Hostname, [switch]$TestCACert, [switch]$TestTrustedParent, [bool]$Force ); if ($Force) { return $FALSE; } if ($TestCACert) { if (Test-Path (Join-Path -Path $CertDirectory -ChildPath 'ca.crt')) { Write-IcingaConsoleNotice 'Your ca.crt is present. No generation or fetching required'; return $TRUE; } else { Write-IcingaConsoleWarning 'Your ca.crt is not present. Manually copy or fetching from your Icinga CA is required.'; return $FALSE; } } if ($TestTrustedParent) { if (Test-Path (Join-Path -Path $CertDirectory -ChildPath 'trusted-parent.crt')) { Write-IcingaConsoleNotice 'Your trusted-parent.crt is present. No fetching or generation required'; return $TRUE; } else { Write-IcingaConsoleWarning 'Your trusted master certificate is not present. Fetching from your CA server is required'; return $FALSE; } } if ((-Not (Test-Path ((Join-Path -Path $CertDirectory -ChildPath $Hostname) + '.key'))) ` -Or -Not (Test-Path ((Join-Path -Path $CertDirectory -ChildPath $Hostname) + '.crt'))) { return $FALSE; } [string]$hostCRT = [string]::Format('{0}.crt', $Hostname); [string]$hostKEY = [string]::Format('{0}.key', $Hostname); [bool]$CertNameInvalid = $FALSE; $certificates = Get-ChildItem -Path $CertDirectory; # Now loop each file and match their name with our hostname foreach ($cert in $certificates) { if ($cert.Name.toLower() -eq $hostCRT.toLower() -Or $cert.Name.toLower() -eq $hostKEY.toLower()) { $file = $cert.Name.Replace('.key', '').Replace('.crt', ''); if (-Not ($file -clike $Hostname)) { Write-IcingaConsoleWarning ([string]::Format('Certificate file {0} is not matching the hostname {1}. Certificate generation is required.', $cert.Name, $Hostname)); $CertNameInvalid = $TRUE; break; } } } if ($CertNameInvalid) { Remove-Item -Path (Join-Path -Path $CertDirectory -ChildPath $hostCRT) -Force; Remove-Item -Path (Join-Path -Path $CertDirectory -ChildPath $hostKEY) -Force; return $FALSE; } Write-IcingaConsoleNotice 'Icinga host certificates are present and valid. No generation required'; return $TRUE; } function Copy-IcingaAgentCACertificate() { param( [string]$CAPath, [string]$Destination ); # Copy ca.crt from local path or network share to certificate path if ((Test-Path $CAPath)) { Copy-Item -Path $CAPath -Destination (Join-Path -Path $Destination -ChildPath 'ca.crt') | Out-Null; Write-IcingaConsoleNotice ([string]::Format('Copied ca.crt from "{0}" to "{1}', $CAPath, $Destination)); } else { Set-IcingaTLSVersion; # It could also be a web resource try { $response = Invoke-IcingaWebRequest $CAPath -UseBasicParsing; [int]$Index = $response.RawContent.IndexOf("`r`n`r`n") + 4; [string]$CAContent = $response.RawContent.SubString( $Index, $response.RawContent.Length - $Index ); Write-IcingaFileSecure -File (Join-Path $Destination -ChildPath 'ca.crt') -Value $CAContent; Write-IcingaConsoleNotice ([string]::Format('Downloaded ca.crt from "{0}" to "{1}', $CAPath, $Destination)) } catch { Write-IcingaConsoleError 'Failed to load any provided ca.crt resource'; return $FALSE; } } return $TRUE; } Export-ModuleMember -Function @('Install-IcingaAgentCertificates'); |