AWS Cloud Operations & Migrations Blog

How to validate authentication with self-signed certificates in Amazon CloudWatch Synthetics

In today’s digital landscape, ensuring optimal application performance is crucial, and Amazon CloudWatch Synthetics enables proactive testing of web applications and APIs. If you are utilizing self-signed certificates and seeking to enhance your monitoring capabilities, this blog post will guide you step-by-step on how to modify the source code of your canary to support self-signed certificates for your endpoints, and the best part is that this method won’t require you to change your authentication process.

We will demonstrate how to utilize the Amazon CloudWatch Synthetics canary with the heartbeat blueprint in order to monitor an application that relies on a self-signed certificate, which are certificates issued and signed by a non-recognized and trusted third-party Certificate Authority (CA).

Furthermore, we recommend you check out our previous blog posts, which covers other methods to monitor APIs. The blog post titled Multi-step API monitoring using Amazon CloudWatch Synthetics provides detailed steps for creating an Amazon CloudWatch Synthetics script to execute multi-step API verification, ensuring the functionality and stability of your APIs. Additionally, our second blog post How to validate authentication using Amazon CloudWatch Synthetics – Part 2 offers step-by-step guidance on updating a synthetic blueprint to monitor applications that requires both the server and the client to present their digital certificates for a mutual authentication (mTLS). These resources will complement your understanding and enable you to build a comprehensive monitoring strategy.

Solution Overview

In this solution, we will demonstrate the steps to create a Heartbeat monitor canary based on an existing blueprint and insert a self-signed certificate within the provided blueprint, allowing the expansion of capabilities provided by the blueprints.

Creating the canary

CloudWatch Synthetics lets you utilize blueprint scripts that are ready to be consumed. However, we must utilize the editor in the console to add the extra code snippets in order to authenticate with a certificate.

To simulate how CloudWatch Synthetics handles authentication, we will use the self-signed.badssl.com website. You can also use your own HTTP endpoint to simulate the same output. The first call will return a failure response, as the certificate has not yet been added. However, the error will be fixed in the next steps.

Steps to create a Canary based on Heartbeat blueprint script:

1. Open the Synthetics menu of the CloudWatch console.

2. Choose Create Canary.

3. Choose Heartbeat monitoring from the blueprints list.

4. Under Name, enter a name for your canary – for example, https-selfsigned-test.

5. Enter the URL https://self-signed.badssl.com/ under the Application or endpoint URL.

6. Please leave all other settings as default, the IAM role with required permissions will be created for you and also a bucket with name cw-syn-results-accountID-region. In case the bucket already exists in the account, you can search for the name cw-syn-results or create your own, more details at Creating a canary.

7. Scroll to the bottom and select Create canary. When the canary is created, it will be displayed in the Canaries list, as shown in Figure 1. For information about utilizing the heartbeat monitor canary blueprint, see in the Heartbeat monitoring Amazon CloudWatch User Guide.


Figure 1: Canaries page of the CloudWatch console

Checking reports

The canary reports show every step and result of the calls. In this case, the canary returned the ERR_CERT_AUTHORITY_INVALID error as shown in Figure 1. This error is expected, as the endpoint uses a certificate that is not issued by a trusted CA and the canary cannot verify the authenticity of the self-signed certificate against a trusted authority.

Figure 2: http-steps-test report failing with the error ERR_CERT_AUTHORITY_INVALID

Downloading the certificate details

To address the authority issue with self-signed certificates, we can download the necessary certificate from the badssl website. This downloaded certificate will serve the purpose of establishing trust, and it will be used by the canary to authenticate API requests made to the self-signed.badssl.com endpoint. The following steps demonstrate how to achieve this using AWS CloudShell using CLI commands, however, these certificates can also be downloaded manually from the badssl website by using a browser.

Note: We do not recommend self-signed certificates unless you are certain you trust the source.

Downloading the public certificate from badssl.com

For this procedure, we will use AWS CloudShell, a browser-based shell that makes it easy to securely manage, explore, and interact with your AWS resources. We recommend CloudShell to run the scripts below. However, you can use your own command line for the same output.

Follow the steps below to download the .pem file from self-signed.badssl.com

1. Open the CloudShell console.

2. Wait for the environment to be created.

3. Copy and paste the script below in the bash console created by the AWS CloudShell.

# Install openssl if you don't have that installed
# Ubuntu users -> sudo apt-get install openssl
sudo yum install openssl -y

# Download the self-signed.badssl.com certificate
openssl s_client -showcerts -connect self-signed.badssl.com:443 -servername self-signed.badssl.com </dev/null | sed -n -e '/-.BEGIN/,/-.END/ p' | awk '{printf "%s%s", (NR>1 ? "\\n" : ""), $0} END {print ""}' > self-signed.pem 

# Get the certificate content to be pasted on the canary code
cat self-signed.pem

4. Choose Paste.


Figure 3: CloudShell popup to paste multi-line text

5. The script above will generate a text with the certificate, so copy the content beginning at —–BEGIN CERTIFICATE—– and ending at —–END CERTIFICATE—– which will be used in the next steps.

Editing the canary script to add the certificate:

To import the certificate, you need to access the created canary and add a variable with the previously copied certificate output.

To edit the code, follow these steps:

1. Open the Synthetics menu of the CloudWatch console.

2. Choose the canary created above – for example, https-selfsigned-test.

3. Choose Actions and then choose Edit.

4. Using the Script Editor box, import the ‘https’ package at the top of the script. This packages will let the script adjust protocol settings to feed in the certificate.

const https = require('https');

5. Insert this block of code below prior to where you attempt to use the ‘page.goto()’ function to access your endpoint. For the blueprint, this is within the loadBlueprint async function between the ‘const sanitizedUrl’ line and the ‘const response = await page.goto...’ line:

await page.setRequestInterception(true);

const cert = '<YOUR_CERTIFICATE_HERE>'

log.info("Injecting request with self-signed certificate.");

page.on("request", (interceptedRequest) => {
  const options = {
    method: interceptedRequest.method(),
    headers: interceptedRequest.headers(),
    body: interceptedRequest.postData(),
    ca: cert,
    cert: cert,
  };
  const request = https
    .request(interceptedRequest.url(), options, function (response) {
      response.on("data", function (data) {
        interceptedRequest.respond({
          status: response.statusCode,
          contentType: response.headers["content-type"],
          headers: response.headers,
          body: data,
        });
      });
    })
    .on("error", function (err) {
      console.error("Unable to call %s", options.uri, err);
      return interceptedRequest.abort("connectionrefused");
    });
  request.end();
});

Note: This code will attempt to intercept the request being made from your canary to your endpoint. From there, it should modify the request options to include your uploaded certificate as the trusted certificate authority.

6. Add the certificate details by replacing the <YOUR_CERTIFICATE_HERE> from the code you just pasted above.

const cert = '<YOUR_CERTIFICATE_HERE>'

7. Choose Save.

Although hard coding certificates is not considered a best practice, we opted for that approach in this blog post to streamline the steps. Additionally, as the certificate is public, the risk is lower. To follow best practices and reduce risks associated when deploying in your environment, you can use AWS Secrets Manager to store the secrets in a secure way as explained on How to validate authentication using Amazon CloudWatch Synthetics – Part 2.

After modifying the code, save and wait for the canary to run again. Next, canary run should be PASSED with the steps tab showing the request status as PASSED.


Figure 4: https-selfsigned-test showing the status as Passed

For users who are already familiar with how to use Synthetics, an advanced option is available that enables the bundling of a canary script with the certificate into a zip file. This approach is explained in the documentation called Writing a Canary Script for Node.JS or Python, providing comprehensive guidance for seamless implementation.

Clean Up

It is important to note that deleting a canary is irreversible and all associated data and settings will be lost. Make sure you have made all necessary backups or extracted all relevant data before proceeding with the deletion.

To delete a CloudWatch Synthetics canary, follow these steps:

1. Open the Synthetics menu of the CloudWatch console.

2. Choose the canary created above – for example, https-selfsigned-test.

3. Choose Actions and then choose Stop.

4. Wait for the canary to change the state to Stopped.

5. Choose Actions again and then choose Delete.

6. Choose the resources you want to delete, for example the Role and Role policy used by the canary.

7. Choose Delete to delete the resources created by the canary.

8. Enter the phrase Delete into the field to confirm that you want to delete the resources created during this workshop.

9. Chose Confirm.

The console will delete the selected resources and display Canary successfully deleted.


Figure 5: Synthetics page showing that the canary was deleted

Conclusion

In this post, we explored how to utilize self-signed certificates with CloudWatch Synthetics, expanding the capabilities of blueprints to connect to APIs, even if the certificate is not signed by a recognized and trusted third-party CA. Now, armed with these insights, you can confidently implement self-signed certificates in your own projects.

To learn more about how to use this feature and all other capabilities, read the CloudWatch Synthetics documentation. Also, the AWS Command Line Interface (CLI) documentation for Synthetics can be found here.

About the authors

Mario Jorge Salheb Leitao

Mario Leitao is a Senior Cloud Architect with the AWS Managed Services (AMS) team based in Sydney. He provides technical guidance, designs and builds cost-effective and efficient solutions for customers ensuring their success on AWS. Prior to working as a Cloud Architect, he worked as a Network Development Engineer in Dublin, Ireland.

Matheus Canela

In his role as Solutions Architect at Amazon Web Services, Matheus advises digital-native companies in the transformation of their technology platforms, helping all levels of engineers to achieve their goals by following best practices. Before he joined AWS, Matheus was a Senior DevOps Consultant at CMD Solutions, which is a Premier Consulting Partner based in Sydney. Matheus has also worked as a developer and a security specialist. Because helping the community is in his DNA, Matheus organizes .NET meetups and helps the IT.BR community, supporting qualified engineers from Brazil to migrate to Australia.

Edgar Yu

Edgar is a Software Development Engineer on the CloudWatch Synthetics team at AWS. His current focus revolves around building and improving software to empower customers to gather more performance data and achieve better monitoring over their web applications.

Outside of AWS, he enjoys food.