From 0c4bb7cb4a9a90710071243e94eab4e1c4ad0d1c Mon Sep 17 00:00:00 2001 From: saltukalakus Date: Fri, 1 Jan 2021 00:24:46 +0300 Subject: [PATCH] Add Slack support --- .env.yml.sample | 8 ++++++++ README.md | 15 +++++++++++---- config.ts | 6 +++++- env.yml | 3 --- handler.ts | 44 +++++++++++++++++++++++++++++++++++--------- package.json | 1 + serverless.yml | 14 ++++++++++++-- 7 files changed, 72 insertions(+), 19 deletions(-) create mode 100644 .env.yml.sample delete mode 100644 env.yml diff --git a/.env.yml.sample b/.env.yml.sample new file mode 100644 index 0000000..2c55a30 --- /dev/null +++ b/.env.yml.sample @@ -0,0 +1,8 @@ +APP_AWS_REGION: us-east-1 +APP_OIDC_IAM_ARN: arn:aws:iam::xxxxxxxx:oidc-provider/login-domain.com +OIDC_LOGIN_DOMAIN: login-domain.com +SLACK_WEB_HOOK: https://hooks.slack.com/services/xxxxx/yyyyy/zzzzzzzzzzzz +STARTING_UPDATE_MSG: Starting the AWS OIDC thumprint update +UPDATE_COMPLETED_MSG: AWS OIDC thumprint successfully updated +ERROR_MSG: AWS OIDC thumprint monitor Lambda function failed check CloudWatch logs +RUN_LAMBDA_EVERY_X_MIN: 5 \ No newline at end of file diff --git a/README.md b/README.md index 8311ca4..a575b13 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,20 @@ # aws-oidc-thumbprint -A lambda function to update the AWS OIDC Identity Provider thumbprint +AWS OIDC Identity Provider needs to pin the login domain certificate. You may find more details regarding this feature [here](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_create_oidc_verify-thumbprint.html) +This tool helps to avoid service distribution by updating the AWS configuration for the certificate thumbprint if the login domain certificate changes. This is useful aspecially if you don't have a control for when the certificate is rotated for the identity provider login domain. E.g. if you are using an identity as a service solution (IaaS) like Auth0, Okta, Azure you may not have the control for the certificate rotation. + +The tool is build as a Lambda function which runs every X minutes configured with RUN_LAMBDA_EVERY_X_MIN env variable to check the cert changes and update the thumbprint if needed. + +The script sends logs to [AWS CloudWatch](https://aws.amazon.com/cloudwatch/) for certificate rotation. Optionally, you can also send notifications to Slack with [Incomming Webhooks](https://api.slack.com/messaging/webhooks). + +**By using this tool you are working-around a security feature. Though it may not be very common to pin the login domain certicate, you are accepting the associated the risks.** ## Conf -Add files for the env variables. +Configure the env variables, by copying the template as .env.yml and fill the necessary variables. ```bash -mv env.yml .env.yml +mv .env.yml.sample .env.yml ``` ## Setup @@ -16,7 +23,7 @@ mv env.yml .env.yml yarn ``` -## Deploy +## Deploy to AWS with Serverless ```bash serverless deploy diff --git a/config.ts b/config.ts index 9e4c7ec..fdeaae5 100644 --- a/config.ts +++ b/config.ts @@ -1,7 +1,11 @@ const Conf = { APP_AWS_REGION: process.env.APP_AWS_REGION, APP_OIDC_IAM_ARN: process.env.APP_OIDC_IAM_ARN, - OIDC_LOGIN_DOMAIN: process.env.OIDC_LOGIN_DOMAIN + OIDC_LOGIN_DOMAIN: process.env.OIDC_LOGIN_DOMAIN, + SLACK_WEB_HOOK: process.env.SLACK_WEB_HOOK, + STARTING_UPDATE_MSG: process.env.STARTING_UPDATE_MSG, + UPDATE_COMPLETED_MSG: process.env.UPDATE_COMPLETED_MSG, + ERROR_MSG: process.env.ERROR_MSG } function Config() { diff --git a/env.yml b/env.yml deleted file mode 100644 index 15bef5f..0000000 --- a/env.yml +++ /dev/null @@ -1,3 +0,0 @@ -APP_AWS_REGION: us-east-1 -APP_OIDC_IAM_ARN: arn:aws:iam::xxxxxxxx:oidc-provider/login-domain.com -OIDC_LOGIN_DOMAIN: login-domain.com \ No newline at end of file diff --git a/handler.ts b/handler.ts index 5d1d0d9..5b169b8 100644 --- a/handler.ts +++ b/handler.ts @@ -1,13 +1,16 @@ -"use strict"; import AWS from "aws-sdk"; +import axios from 'axios'; const sslCertificate = require('get-ssl-certificate-fork'); + import Config from './config'; -exports.run = async (event, context) => { +exports.run = async () => { const conf = Config(); + const headers = { + 'Content-Type': 'application/json' + } const cert = await sslCertificate.get(conf.OIDC_LOGIN_DOMAIN, 5000, 443, "https:", true); let fingerprint = cert.issuerCertificate.fingerprint.toLowerCase().replace(/:/g, ''); - console.log(fingerprint); AWS.config.update({ region: conf.APP_AWS_REGION }) const iam = new AWS.IAM() @@ -15,20 +18,43 @@ exports.run = async (event, context) => { OpenIDConnectProviderArn: conf.APP_OIDC_IAM_ARN }; iam.getOpenIDConnectProvider(options, (err, data) => { - if (err) console.log(err, err.stack); + if (err) { + console.log(conf.ERROR_MSG, err, err.stack); + if (conf.SLACK_WEB_HOOK) { + axios.post(conf.SLACK_WEB_HOOK, { "text": conf.ERROR_MSG }, { + headers: headers + }).then(() => { }).catch(() => { }); + } + } else { - console.dir(data); - console.dir(data.ThumbprintList); if (data.ThumbprintList.indexOf(fingerprint) === -1) { - console.log("UPDATE AWS CERT!!!"); + console.log(conf.STARTING_UPDATE_MSG); + if (conf.SLACK_WEB_HOOK) { + axios.post(conf.SLACK_WEB_HOOK, { "text": conf.STARTING_UPDATE_MSG }, { + headers: headers + }).then(() => { }).catch(() => { }); + } data.ThumbprintList[0] = fingerprint; const updateParams = { OpenIDConnectProviderArn: conf.APP_OIDC_IAM_ARN, ThumbprintList: data.ThumbprintList }; iam.updateOpenIDConnectProviderThumbprint(updateParams, function (err, data) { - if (err) console.log(err, err.stack); // an error occurred - else console.log('Cert successfully updated', data); // successful response + if (err) { + console.log(conf.ERROR_MSG, err, err.stack); + if (conf.SLACK_WEB_HOOK) { + axios.post(conf.SLACK_WEB_HOOK, { "text": conf.ERROR_MSG }, { + headers: headers + }).then(() => { }).catch(() => { }); + } + } else { + console.log(conf.UPDATE_COMPLETED_MSG, data); + if (conf.SLACK_WEB_HOOK) { + axios.post(conf.SLACK_WEB_HOOK, { "text": conf.UPDATE_COMPLETED_MSG }, { + headers: headers + }).then(() => { }).catch(() => { }); + } + } }); } } diff --git a/package.json b/package.json index 4b0b364..3b93ee6 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,7 @@ "author": "saltukalakus@gmail.com", "license": "MIT", "dependencies": { + "axios": "^0.21.1", "get-ssl-certificate-fork": "^2.3.5" }, "devDependencies": { diff --git a/serverless.yml b/serverless.yml index 92bd9b8..088df8d 100644 --- a/serverless.yml +++ b/serverless.yml @@ -11,6 +11,7 @@ custom: includeModules: forceInclude: - get-ssl-certificate-fork + - axios provider: name: aws @@ -20,6 +21,10 @@ provider: APP_AWS_REGION: ${file(.env.yml):APP_AWS_REGION} APP_OIDC_IAM_ARN: ${file(.env.yml):APP_OIDC_IAM_ARN} OIDC_LOGIN_DOMAIN: ${file(.env.yml):OIDC_LOGIN_DOMAIN} + SLACK_WEB_HOOK: ${file(.env.yml):SLACK_WEB_HOOK} + STARTING_UPDATE_MSG: ${file(.env.yml):STARTING_UPDATE_MSG} + UPDATE_COMPLETED_MSG: ${file(.env.yml):UPDATE_COMPLETED_MSG} + ERROR_MSG: ${file(.env.yml):ERROR_MSG} iamRoleStatements: - Effect: Allow @@ -37,5 +42,10 @@ functions: cron: handler: handler.run events: - # Invoke Lambda function every 5 minute - - schedule: cron(0/1 * * * ? *) + # Invoke Lambda function every X minute + - schedule: cron(0/${file(.env.yml):RUN_LAMBDA_EVERY_X_MIN} * * * ? *) + - http: + path: api/run + method: post + integration: lambda + cors: true \ No newline at end of file