How to keep an eye on the SAML self operator certificates’ expiration in an Amazon Cognito consumer pool
With Amazon Cognitouser pools, you can configure third-party SAML identity providers (IdPs) so that users can log in by using the IdP credentials. The Amazon Cognito user pool manages the federation and handling of tokens returned by a configured SAML IdP. It uses the public certificate of the SAML IdP to verify the signature in the SAML assertion returned by the IdP. Public certificates have an expiry date, and an expired public certificate will result in a SAML user federation failing because it can no longer be used for signature verification. To avoid user authentication failures, you must monitor and rotate SAML public certificates before expiration.
<p>You can configure SAML IdPs in an Amazon Cognito user pool by using a SAML metadata document or a URL that points to the metadata document. If you use the SAML metadata document option, you must manually upload the SAML metadata. If you use the URL option, Amazon Cognito downloads the metadata from the URL and automatically configures the SAML IdP. In either scenario, if you don’t rotate the SAML certificate before expiration, users can’t log in using that SAML IdP.</p> <p>In this blog post, I will show you how to monitor SAML certificates that are about to expire or already expired in an Amazon Cognito user pool by using an <a href="https://aws.amazon.com/lambda/" target="_blank" rel="noopener">AWS Lambda</a> function initiated by an <a href="https://aws.amazon.com/eventbridge/" target="_blank" rel="noopener">Amazon EventBridge</a> rule.</p> <h2>Solution overview</h2> <p>In this section, you will learn how to configure a <a href="https://docs.aws.amazon.com/lambda/latest/dg/welcome.html" target="_blank" rel="noopener">Lambda operation</a> that checks the validity period of the SAML IdP certificates in an Amazon Cognito user pool, logs the findings to <a href="https://aws.amazon.com/security-hub/" target="_blank" rel="noopener">AWS Security Hub</a>, and sends out an <a href="https://aws.amazon.com/sns/" target="_blank" rel="noopener">Amazon Simple Notification Service (Amazon SNS)</a> notification with the list of certificates that are about to expire or have already expired. This Lambda operation is invoked by an <a href="https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-rules.html" target="_blank" rel="noopener">EventBridge regulation</a> that uses a rate or cron expression and runs on a defined schedule. For example, if the <a href="https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-create-rule-schedule.html#eb-rate-expressions" target="_blank" rel="noopener">rate expression</a> is defined as 1 day, the <a href="https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-rules.html" target="_blank" rel="noopener">EventBridge regulation</a> initiates the Lambda operation once each day. Figure 1 shows an overview of this process.</p> <div id="attachment_29257" class="wp-caption aligncenter"> <img aria-describedby="caption-attachment-29257" src="https://infracom.com.sg/wp-content/uploads/2023/05/img1-6-1024x638-1.png" alt="Figure 1: Lambda operation initiated by EventBridge regulation" width="760" class="size-large wp-image-29257"> <p id="caption-attachment-29257" class="wp-caption-text">Figure 1: Lambda operation initiated by EventBridge regulation</p> </div> <p>As shown in Figure 1, this process involves the following steps:</p> <ol> <li>EventBridge runs a <a href="https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-create-rule-schedule.html" target="_blank" rel="noopener">rule</a> using a <a href="https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-create-rule-schedule.html#eb-rate-expressions" target="_blank" rel="noopener">rate expression</a> or <a href="https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-create-rule-schedule.html#eb-cron-expressions" target="_blank" rel="noopener">cron expression</a> and invokes the Lambda operation.</li> <li>The <a href="https://docs.aws.amazon.com/lambda/" target="_blank" rel="noopener">Lambda operation</a> performs the following tasks: <ol> <li>Gets the list of SAML IdPs and corresponding X509 certificates.</li> <li>Verifies if the X509 certificates are about to expire or already expired based on the dates in the certificate. </li> </ol> </li> <li>Based on the results of step 2, the Lambda operation logs the findings in AWS Security Hub. Each finding shows the SAML certificate that is about to expire or is already expired.</li> <li>Based on the results of step 2, the Lambda operation publishes a notification to the <a href="https://docs.aws.amazon.com/sns/" target="_blank" rel="noopener">Amazon SNS</a> topic with the certificate expiration details. For example, if <span>CERT_EXPIRY_DAYS=60</span>, the details of SAML certificates that are going to expire within 60 days or are already expired are published in the SNS notification.</li> <li>Amazon SNS sends messages to the subscribers of the topic, such as an email address.</li> </ol> <h2>Prerequisites</h2> <p>For this setup, you will need to have the following in place:</p> <h2>Implementation details</h2> <p>In this section, we will walk you through how to deploy the Lambda operation and configure an EventBridge regulation that invokes the Lambda operation.</p> <h3 id="step_1">Step 1: Create the Node.js Lambda package</h3> <ol> <li>Open a command line terminal or shell.</li> <li>Create a folder named <span>saml-certificate-expiration-monitoring</span>.</li> <li>Install the fast-xml-parser module by running the following command: <pre><code class="lang-powershell">cd saml-certificate-expiration-monitoring
npm install fast-xml-parser
- Create a file named index.js and paste the following content in the file.
const AWS = require('aws-sdk');const { X509Certificate } = require('crypto');const { XMLParser} = require("fast-xml-parser");const https = require('https');exports.handler = async function(event, context, callback) {
const cognitoUPID = process.env.COGNITO_UPID;const expiryDays = process.env.CERT_EXPIRY_DAYS;const snsTopic = process.env.SNS_TOPIC_ARN;const postToSh = process.env.ENABLE_SH_MONITORING; //Enable security hub monitoringvar securityhub = new AWS.SecurityHub({apiVersion: '2018-10-26'});var shParams = { Findings: []};AWS.config.apiVersions = { cognitoidentityserviceprovider: '2016-04-18',};// Initialize CognitoIdentityServiceProvider.const cognitoidentityserviceprovider = new AWS.CognitoIdentityServiceProvider();let listProvidersParams = { UserPoolId: cognitoUPID /* required */};let hasNext = true;const providerNames = [];while (hasNext) { const listProvidersResp = await cognitoidentityserviceprovider.listIdentityProviders(listProvidersParams).promise(); listProvidersResp['Providers'].forEach(function(provider) { if(provider.ProviderType == 'SAML') { providerNames.push(provider.ProviderName); } }); listProvidersParams.NextToken = listProvidersResp.NextToken; hasNext = !!listProvidersResp.NextToken; //Keep iterating if there are more pages}let describeIdentityProviderParams = { UserPoolId: cognitoUPID /* required */};//Initialize the options for fast-xml-parser //Parse KeyDescriptor as an arrayconst alwaysArray = [ "EntityDescriptor.IDPSSODescriptor.KeyDescriptor"];const options = { removeNSPrefix: true, isArray: (name, jpath, isLeafNode, isAttribute) => { if( alwaysArray.indexOf(jpath) !== -1) return true; }, ignoreDeclaration: true};const parser = new XMLParser(options);let certExpMessage = '';const today = new Date();if(providerNames.length == 0) { console.log("There are no SAML providers in this Cognito user pool. ID : " + cognitoUPID);}for (let provider of providerNames) { describeIdentityProviderParams.ProviderName = provider; const descProviderResp = await cognitoidentityserviceprovider.describeIdentityProvider(describeIdentityProviderParams).promise(); let xml = ''; //Read SAML metadata from Cognito if the file is available. Else, read the SAML metadata from URL if('MetadataFile' in descProviderResp.IdentityProvider.ProviderDetails) { xml = descProviderResp.IdentityProvider.ProviderDetails.MetadataFile; } else { let metadata_promise = getMetadata(descProviderResp.IdentityProvider.ProviderDetails.MetadataURL); xml = await metadata_promise; } let jObj = parser.parse(xml); if('EntityDescriptor' in jObj) { //SAML metadata can have multiple certificates for signature verification. for (let cert of jObj['EntityDescriptor']['IDPSSODescriptor']['KeyDescriptor']) { let certificate = '-----BEGIN CERTIFICATE-----n' + cert['KeyInfo']['X509Data']['X509Certificate'] + 'n-----END CERTIFICATE-----'; let x509cert = new X509Certificate(certificate); console.log("------ Provider : " + provider + "-------"); console.log("Cert Expiry: " + x509cert.validTo); const diffTime = Math.abs(new Date(x509cert.validTo) - today); const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24)); console.log("Days Remaining: " + diffDays); if(diffDays { https.get(url, (response) => { let chunks_of_data = []; response.on('data', (fragments) => { chunks_of_data.push(fragments); }); response.on('end', () => { let response_body = Buffer.concat(chunks_of_data); resolve(response_body.toString()); }); response.on('error', (error) => { reject(error); }); });});
}function logFindingToSh(context, shParams, remediationMsg, certFp, cognitoUPID, provider) { const accountID = context.invokedFunctionArn.split(‘:’)[4]; const region = process.env.AWS_REGION; const sh_product_arn =
arn:aws:securityhub:${region}:${accountID}:product/${accountID}/default
; const today = new Date().toISOString();shParams.Findings.push( { SchemaVersion: “2018-10-08”, AwsAccountId:${accountID}
, /* required / CreatedAt:${today}
, / required / UpdatedAt:${today}
, Title: ‘SAML Certificate expiration’, Description: ‘SAML certificate expiry’, / required / GeneratorId:${context.invokedFunctionArn}
, / required / Id:${cognitoUPID}:${provider}:${certFp}
, / required / ProductArn:${sh_product_arn}
, / required / Severity: { Original: ‘89.0’, Label: ‘HIGH’ }, Types: [ “Software and Configuration Checks/AWS Config Analysis” ], Compliance: {Status: ‘WARNING’}, Resources: [ / required / { Id:${cognitoUPID}
, / required / Type: ‘AWSCognitoUserPool’, / required / Region:${region}
, Details : { Other: { “IdPIdentifier” :${provider}
} } } ], Remediation: { Recommendation: { Text:${remediationMsg}
, Url: `https://console.aws.amazon.com/cognito/v2/idp/user-pools/${cognitoUPID}/sign-in/identity-providers/details/${provider}` } } } );} - To create the implementation package for a.zip file library, you can use a built – in.zip file library power or other third – party zip file utility. If you are using Linux or Mac OS, pass the following statement.
zip -r saml-certificate-expiration-monitoring.zip .
<h3 id="step_2">Step 2: Create an SNS area on Amazon</h3> <ol> <li>Create a standard SNS area on Amazon named <span>saml-certificate-expiration-monitoring-topic</span> for the Lambda operation to use to send out notifications, as described in <a href="https://docs.aws.amazon.com/sns/latest/dg/sns-create-topic.html" target="_blank" rel="noopener">Creating an SNS area on Amazon</a>.</li> <li>Copy the Amazon Resource Name (ARN) for Amazon SNS. Later in this post, you will use this ARN in the <a href="https://aws.amazon.com/iam/" target="_blank" rel="noopener">AWS Identity and Access Management (IAM)</a> policy and Lambda environment variable configuration.</li> <li>After you create the SNS area on Amazon, create <a href="https://docs.aws.amazon.com/sns/latest/dg/sns-email-notifications.html" target="_blank" rel="noopener">email subscribers</a> to this topic.</li> </ol> <h3 id="step_3">Step 3: Configure the IAM role and policies and deploy the Lambda operation</h3> <ol> <li>In the IAM documentation, review the section <a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_create-console.html#access_policies_create-json-editor" target="_blank" rel="noopener">Creating policies on the JSON tab</a>. Then, using those instructions, use the following template to create an IAM policy named <span>lambda-saml-certificate-expiration-monitoring-function-policy</span> for the Lambda role to use. Replace <span></span> with your Region, <span></span> with your <a href="https://docs.aws.amazon.com/accounts/latest/reference/manage-acct-identifiers.html#FindAccountId" target="_blank" rel="noopener">AWS account ID</a>, <span></span> with the Amazon SNS ARN from <a href="https://aws.amazon.com/blogs/security/how-to-monitor-the-expiration-of-saml-identity-provider-certificates-in-an-amazon-cognito-user-pool/#step_2" target="_blank" rel="noopener">Step 2: Create an SNS area on Amazon</a>, and <span></span> with your Amazon Cognito user pool ID that you want to monitor. <div class="hide-language"> <pre><code class="lang-text">{"Version": "2012-10-17","Statement": [ { "Sid": "AllowLambdaToCreateGroup", "Effect": "Allow", "Action": "logs:CreateLogGroup", "Resource": "arn:aws:logs:<span></span>:<span></span>:" }, { "Sid": "AllowLambdaToPutLogs", "Effect": "Allow", "Action": [ "logs:CreateLogStream", "logs:PutLogEvents" ], "Resource": [ "arn:aws:logs:<span></span>:<span></span>:log-group:/aws/lambda/saml-certificate-expiration-monitoring:*" ] }, { "Sid": "AllowLambdaToGetCognitoIDPDetails", "Effect": "Allow", "Action": [ "cognito-idp:DescribeIdentityProvider", "cognito-idp:ListIdentityProviders", "cognito-idp:GetIdentityProviderByIdentifier" ], "Resource": "arn:aws:cognito-idp:<span></span>:<span></span>:userpool/<span></span>" }, { "Sid": "AllowLambdaToPublishToSNS", "Effect": "Allow", "Action": "SNS:Publish", "Resource": "<span></span>" } , { "Sid": "AllowLambdaToPublishToSecurityHub", "Effect": "Allow", "Action": [ "SecurityHub:BatchImportFindings" ], "Resource": "arn:aws:securityhub:<span></span>:<span></span>:product/<span></span>/default" }]
}
- Create a purpose to grant permissions to an AWS program after the policy has been created by following the instructions in that role. To take on the role and connect the plan beta – https, certificates, loss, monitoring, function, and policy that you created in step 1 of this section, select Lambdas as the service. Create a role by naming it lambda-saml-certificate-expiration-monitoring-function-role, and then describe the role.
- Examine the Lambda documentation’s area,” Create an Android functionality with the system.” The Lambda act should then be created, with the following methods:
- To produce the act, select Author from scratch under the Create function.
- Enter the act name for the saml certificate, expiration – monitoring, and Node for Runtime. 16. x. js
- Increase Change default murder part for the Execution part field, choose Use an existing role, and then choose the role created in step 2 of this section.
- To access the Designer and add the zip file that was made in Step 1: Establish the Node, select the Create purpose. Lambda js item.
- You ought to view the catalog. the Lambda console’s java password.
- You must change the latency period after the Lambda feature is created. The Lambda latency should be set to 10 hours. Visit the delay entry in the console’s Configuring functions for more details. See How do I fix Lambda feature invocation timeout errors if you receive one.
- Install the Lambda feature if you make any code change after uploading.
Establish an EventBridge regulation in step four.
- To create a policy known as http – certificates, loss, testing, and act, follow the instructions for creating an Amazon EventBridge regulation that runs on schedule. To start the party, you can use a 24-hour price expression. The Lambda feature will only be used when daily under this rule.
- Find the AWS Lambda program to select a goal.
- Choose the saml-certificate-expiration-monitoring feature that you installed in Step 3 for the Lambda operationality. Then, set up the IAM role and policies before deploying the function itself.
Step 5: Run the Lambda feature.
- Choose the earlier-created feature in the Lambda console, then setup the following environment variables:
- Make an environment variable with the name CERT_ EXPIRY_ DAYS. This specifies how many days of lead time you should have before the certification passing telling is sent.
- Create a COGNITO_ UPID environment variable. This identifies the required monitoring Amazon Cognito individual pool Card.
- Create an environment variable with the name SNS_ TOPIC_ ARN and set it to the ARN from Step 2: Create a topic for the Amazon SN.
- Set the environment variable ENABLE_ SH_ MONITORING to true or false. The Lambda act will record the results in AWS Security Hub if you set it to correct.
- Configure a test event for the Lambda operation by using the default template and name it TC1, as shown in Figure 2.
- Check the Lambda operation using the TC1 try condition. Determine the Amazon CloudWatchlogs to make sure the Lambda feature worked properly. The device log communications from the Lambda operation may be visible. You will see a list of results in AWS Security Hub for certificates with an expiration of less than or equal to the value of the CERT_ EXPIRY_ DAYS environment variable if ENABLE_ SH_ MONITORING is set to true in the Lambda environment variables. Additionally, an email will be sent to each Amazon SNS area subscriber.
Cleanup
If you don’t require them, remove the methods listed below and turn off AWS Security Hub to prevent future costs.
- Lambda operation
- EventBridge regulation
- CloudWatch logs associated with the Lambda operation
- SNS area on Amazon
- IAM role and policy that you created for the Lambda operation
Conclusion
It can be difficult to keep an eye on an Amazon Cognito individual pool with lots of SAML IdPs. Users cannot log in using a SAML IdP qualification if it expires. This article outlines how to keep an eye on your SAML IdP certificates and update Amazon Cognito consumer pool administrators when a certification is about to expire so you can actively work to turn the certificate with them. I advise you to put these, or similar, controls in place now that you are aware of the advantages of keeping an eye on your IdP credentials for expiration in order to be alerted to these events before they happen.
Please leave comments in the paragraph below if you have any feedback on this article. Create a new ribbon on the Amazon Cognito re: Post or get in touch with AWS Assistance if you have any doubts about this article.
Want to learn more about AWS Security? Become a follower on Twitter.
<!-- '"` -->
You must be logged in to post a comment.