From b8083035fe3e402f05e212b7d4090e403e90085b Mon Sep 17 00:00:00 2001 From: David Horsman Date: Fri, 22 Jan 2021 03:13:13 +0000 Subject: [PATCH] fix(core): windows cloudwatch agent install script Fixes #295 This fixes a bug that is preventing the deployment of any worker instance that is using an AMI with a Windows OS. --- .../powershell/configureCloudWatchAgent.ps1 | 125 ++++++++++++------ .../aws-rfdk/lib/core/test/asset-constants.ts | 4 +- 2 files changed, 86 insertions(+), 43 deletions(-) diff --git a/packages/aws-rfdk/lib/core/scripts/powershell/configureCloudWatchAgent.ps1 b/packages/aws-rfdk/lib/core/scripts/powershell/configureCloudWatchAgent.ps1 index f0793dc61..fdff6a0fb 100644 --- a/packages/aws-rfdk/lib/core/scripts/powershell/configureCloudWatchAgent.ps1 +++ b/packages/aws-rfdk/lib/core/scripts/powershell/configureCloudWatchAgent.ps1 @@ -16,62 +16,105 @@ param ( $ErrorActionPreference = "Stop" -$file = "$env:temp\amazon-cloudwatch-agent.msi" +Write-Output "Starting CloudWatch installation and configuration script." -# We hardcode the version number here since the verification below is tightly coupled with this specific version +$is_cloudwatch_installed = $False try { - Read-S3Object -BucketName amazoncloudwatch-agent -Key windows/amd64/1.242486.0/amazon-cloudwatch-agent.msi -File $file -Region us-east-1 + # If this command doesn't throw an error, we have the CloudWatch agent installed already + $status = & $Env:ProgramFiles\Amazon\AmazonCloudWatchAgent\amazon-cloudwatch-agent-ctl.ps1 -m ec2 -a status + $is_cloudwatch_installed = $True + Write-Output "Found CloudWatch agent already installed, skipping installation." } catch { - # Fallback to the latest version (this is the case when the above version is currently "latest") - Write-Output "Initial attempt to download Amazon CloudWatch agent failed. Falling back to the latest version." + Write-Output "CloudWatch agent is not already installed, proceeding with installation." +} + +if (-Not $is_cloudwatch_installed) { + $cwa_installer = "$env:temp\amazon-cloudwatch-agent.msi" try { - Read-S3Object -BucketName amazoncloudwatch-agent -Key windows/amd64/latest/amazon-cloudwatch-agent.msi -File $file -Region us-east-1 + Read-S3Object -BucketName amazoncloudwatch-agent -Key windows/amd64/latest/amazon-cloudwatch-agent.msi -File $cwa_installer -Region us-east-1 } catch { - Write-Output "Failed to download CloudWatchAgent installer." + Write-Output "Failed to download CloudWatch agent installer." Exit 1 } -} -# The below verification is tied to the version of the msi installer -# If the installer version changes, the below verification could break and should be updated accordingly. -# See https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/verify-CloudWatch-Agent-Package-Signature.html -if (-Not $s) { - $sig = Get-AuthenticodeSignature $file - $status = $sig | Select-Object -ExpandProperty 'Status' - if ( $status -ne 'Valid' ) { - Write-Output "CloudWatchAgent installer does not have a valid signature." + $cwa_installer_sig = "$env:temp\amazon-cloudwatch-agent.msi.sig" + try { + Read-S3Object -BucketName amazoncloudwatch-agent -Key windows/amd64/latest/amazon-cloudwatch-agent.msi.sig -File $cwa_installer_sig -Region us-east-1 + } catch { + Write-Output "Failed to download CloudWatch agent installer signature file." Exit 1 } - # CA Certificate is: - # - Subject: CN=DigiCert Timestamp Responder, O=DigiCert, C=US - # - Valid: [Not After] 10/21/2024 7:00:00 PM - # - Thumbprint: 614D271D9102E30169822487FDE5DE00A352B01D - $ca = $sig | Select-Object -ExpandProperty 'TimeStamperCertificate' - $ca_thumbprint = $ca | Select-Object -ExpandProperty 'Thumbprint' - if ( $ca_thumbprint -ne '614D271D9102E30169822487FDE5DE00A352B01D' ) { - Write-Output "CA Thumbprint failed verification. Has the CA certificate been changed? If so, then please submit a PR to fix this." - Write-Output $( $ca | Format-List ) - Exit 1 - } + if (-Not $s) { + $gpg_keyring = "$env:temp\keyring.gpg" - # Amazon.com certificate is: - # - Subject: 'CN=Amazon.com Services LLC, OU=Software Services, O=Amazon.com Services LLC, L=Seattle, S=Washington, C=US' - # We do not check the Thumbprint because it will change when the certificate is rotated. This seems to be yearly. - $cert = $sig | Select-Object -ExpandProperty 'SignerCertificate' - $cert_subject = $cert | Select-Object -ExpandProperty 'Subject' - if ( $cert_subject -ne 'CN=Amazon.com Services LLC, OU=Software Services, O=Amazon.com Services LLC, L=Seattle, S=Washington, C=US' ) { - Write-Output "Amazon.com is not the issuer of this installer. If this is the correct then please submit a PR to fix this." - Write-Output $( $cert | Format-List ) - Exit 1 + # Download GPG + $gpg_installer = "$env:temp\gnupg-w32-2.2.27_20210111.exe" + wget https://gnupg.org/ftp/gcrypt/binary/gnupg-w32-2.2.27_20210111.exe -OutFile $gpg_installer + + # Verify GPG + $gpg_sig = Get-AuthenticodeSignature $gpg_installer + $status = $gpg_sig | Select-Object -ExpandProperty 'Status' + if ( $status -ne 'Valid' ) { + Write-Output "GPG installer does not have a valid signature." + Exit 1 + } + $sha256_expected = '5D89E239790822711EAE2899467A764879D21440AB68E9413452FA96CEDEBA50' + $sha256 = Get-FileHash $gpg_installer -Algorithm SHA256 + if ( $sha256 -inotmatch $sha256_expected) { + Write-Output "GPG failed checksum verification. Expected sha256 to equal $sha256_expected but got:" + Write-Output $sha256 + Exit 1 + } + + # Install GPG + Start-Process -Wait -FilePath $gpg_installer -ArgumentList "/S /v/qn" -PassThru + + # Refresh the PATH so gpg is available + $env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + + # Download Amazon's public key and import it to GPG's keyring + $cloudwatch_pub_key = "$env:temp\amazon-cloudwatch-agent.gpg" + Read-S3Object -BucketName amazoncloudwatch-agent -Key assets/amazon-cloudwatch-agent.gpg -File $cloudwatch_pub_key -Region us-east-1 + gpg --no-default-keyring --keyring $gpg_keyring --import $cloudwatch_pub_key + + # Verify that the imported key has the correct fingerprint + $fingerprint = '937616F3450B7D806CBD9725D58167303B789C72' + $keys = gpg --no-default-keyring --keyring $gpg_keyring -k + $keys = ($keys | Out-String).Trim() -replace "\r", "" -replace "\n", "" -replace "\[", "" -replace "\]", "" + + if ($keys -inotlike "*$fingerprint*") { + Write-Output "Expected CloudWatch agent's public key to equal $fingerprint but got:" + Get-Content $keys + Exit 1 + } + + # Now that we have the public key on the keyring, we can use gpg to perform the verification of the installer with the signature file. + # We will write the output to file and then perform a text search to make sure there's a good signature present in it + $gpg_output = "$env:temp\gpg_out.txt" + Start-Process gpg -ArgumentList " --no-default-keyring --keyring $gpg_keyring --verify $cwa_installer_sig $cwa_installer" ` + -wait -NoNewWindow -PassThru -RedirectStandardError $gpg_output + + $verification = Select-String -Path $gpg_output -Pattern 'Good signature from "Amazon CloudWatch Agent"' | Select-Object -ExpandProperty Matches -First 1 + if (-Not $verification) { + Write-Output "Could not verify CloudWatch agent's signature file with GPG." + Get-Content $gpg_output + Exit 1 + } } -} -# Install the agent -Start-Process "msiexec.exe" -ArgumentList "/i $env:temp\amazon-cloudwatch-agent.msi /qn /norestart" -Wait -Passthru -NoNewWindow + # Install the agent + Start-Process "msiexec.exe" -ArgumentList "/i $cwa_installer /qn /norestart" -Wait -Passthru -NoNewWindow + + Remove-Item -Path $cloudwatch_pub_key -Force + Remove-Item -Path $cwa_installer -Force + Remove-Item -Path $cwa_installer_sig -Force + Remove-Item -Path $gpg_installer -Force + Remove-Item -Path $gpg_output -Force + Remove-Item -Path $gpg_keyring -Force +} # Configure the agent from an ssm parameter-store parameter. & 'C:/Program Files/Amazon/AmazonCloudWatchAgent/amazon-cloudwatch-agent-ctl.ps1' -a append-config -m ec2 -c ssm:$ssmParameterName -s -Remove-Item -Path $file -Force -Write-Output "CloudWatchAgent has been successfully installed and configured" +Write-Output "CloudWatch agent has been successfully installed and configured" diff --git a/packages/aws-rfdk/lib/core/test/asset-constants.ts b/packages/aws-rfdk/lib/core/test/asset-constants.ts index 0b529463b..22495dfe9 100644 --- a/packages/aws-rfdk/lib/core/test/asset-constants.ts +++ b/packages/aws-rfdk/lib/core/test/asset-constants.ts @@ -13,8 +13,8 @@ export const CWA_ASSET_LINUX = { // ConfigureCloudWatchAgent.ps1 export const CWA_ASSET_WINDOWS = { - Bucket: 'AssetParameters05415690a7593cdde72555787eaac1d784dd3173e6083f23f83dc795bfe1741fS3Bucket0E53698F', - Key: 'AssetParameters05415690a7593cdde72555787eaac1d784dd3173e6083f23f83dc795bfe1741fS3VersionKeyE92C9DEB', + Bucket: 'AssetParameters07782992a7a530f8752341d912c95ba2fe3f0a212d413b5d097959c51ea8e2ecS3Bucket95C4512E', + Key: 'AssetParameters07782992a7a530f8752341d912c95ba2fe3f0a212d413b5d097959c51ea8e2ecS3VersionKey3DB883AC', }; // mountEbsBlockVolume.sh + metadataUtilities.sh + ec2-certificates.crt