Wednesday, March 25, 2020

AWS Custom Config Cross-Account

How to AWS Custom Config Cross-Account

Lambda - Python

Account 999999999
  • This is where you'll host the Lambda code
Account 888888888
  • This is where you'll host the Config Rule

Account 999999999

Follow the previous instruction for the Python code, update the lambda_handler with following:

def lambda_handler(event, context):
    evaluations = []
    test_mode = False
    thisRegion = 'us-east-1'
    print('Event: ',event)
    invoking_event = json.loads(event["invokingEvent"])
    print('Invoking Event: ',invoking_event)
    notification_time = invoking_event["notificationCreationTime"]
    print('Invoked Time: ', notification_time)
    result_token = event["resultToken"]
    print('Result Token: ', result_token)
    # In our test event, we have defined the result_token to be XYZ
    if result_token == 'XYZ':
        test_mode = True
        
    # this is passed to us from Config Rule, if this exists, then Assume Role
    ruleParameters = json.loads(event["ruleParameters"])
    if 'executionRole' in ruleParameters.keys():
        print('Assume Role')
        executionRole = ruleParameters["executionRole"]
        print(executionRole)
        sts_client = boto3.client('sts')
        assume_role_response = sts_client.assume_role(RoleArn=executionRole, RoleSessionName="configLambdaExecution")
        credentials = assume_role_response['Credentials']
        config = boto3.client("config", region_name=thisRegion,
                        aws_access_key_id=credentials['AccessKeyId'],
                        aws_secret_access_key=credentials['SecretAccessKey'],
                        aws_session_token=credentials['SessionToken']
                       )
        
    else:
        credentials = []
        config = boto3.client("config")
        print('Self Run')
    # pass in credential and time stamp and get back eval result
    evaluations = evaluate_compliance(notification_time, credentials)
    print(evaluations)
    print(result_token)
    result = config.put_evaluations(
            Evaluations = evaluations,
            ResultToken = result_token,
            TestMode = test_mode
            )
    
    metaData = result["ResponseMetadata"]
    statusCode = metaData["HTTPStatusCode"]
    return {
        'statusCode': statusCode,
        'body': json.dumps(result)
    }

Be sure your execution role for this function has at least the following. The first statement allows the function to send logs to cloudwatch. The second statement allows this function to assume the role of Config_Role from another account.

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogStream",
                "logs:PutLogEvents"
            ],
            "Resource": "arn:aws:logs:us-east-1:999999999:log-group:/aws/lambda/config_test:*"
        },
        {
            "Sid": "VisualEditor1",
            "Effect": "Allow",
            "Action": "logs:CreateLogGroup",
            "Resource": "arn:aws:logs:us-east-1:999999999:*"
        }
    ]
}

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": "sts:AssumeRole",
            "Resource": "arn:aws:iam::888888888:role/config_role
        }
    ]
}

Add a Resource-based policy to your Lambda function so that it can be used by another account. This can only be done via CLI or API call.
Run this CLI command using credential of account 99999999

aws lambda add-permission 
  --function-name config_test
  --region us-east-1 
  --statement-id 1001 
  --action "lambda:InvokeFunction" 
  --principal config.amazonaws.com 
  --source-account 888888888 


Account 888888888

Create a new role in this account with at least following permission. Be sure to have specific permission so that you can evaluate your resources.

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "config:PutEvaluations"
            ],
            "Resource": [
                "*"
            ]
        }
    ]
}

The new role also needs trust relationship to ALLOW it be assumed by the Lambda function

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::999999999:role/service-role/lambdaConfigRole"
      },
      "Action": "sts:AssumeRole",
      "Condition": {}
    }
  ]
}

Now create the new Config Rule
Provide the Lambda function that you created in account 99999999:


Set your trigger type and resource type(s).

Fill out the Rule parameter with the role that you want the Lambda function to use:

Done. Now try running the config rule manually by clicking on the blue Re-evaluate button in account 8888888888 and watch the Cloudwatch logs in account 999999999.

No comments:

Post a Comment

AWS WAF log4j query

How to query AWS WAF log for log4j attacks 1. Setup your Athena table using this instruction https://docs.aws.amazon.com/athena/latest/ug/wa...