<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	
	>
<channel>
	<title>
	Comments on: Using the Application Load Balancer and WAF to replace CloudFront Security Groups	</title>
	<atom:link href="https://cloudar.be/awsblog/using-the-application-load-balancer-and-waf-to-replace-cloudfront-security-groups/feed/" rel="self" type="application/rss+xml" />
	<link>https://cloudar.be/awsblog/using-the-application-load-balancer-and-waf-to-replace-cloudfront-security-groups/</link>
	<description>100% Focus On AWS // 100% Customer Obsession</description>
	<lastBuildDate>Mon, 12 Aug 2024 08:17:45 +0000</lastBuildDate>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.4</generator>
	<item>
		<title>
		By: Jorge		</title>
		<link>https://cloudar.be/awsblog/using-the-application-load-balancer-and-waf-to-replace-cloudfront-security-groups/#comment-37</link>

		<dc:creator><![CDATA[Jorge]]></dc:creator>
		<pubDate>Thu, 30 Apr 2020 12:20:55 +0000</pubDate>
		<guid isPermaLink="false">https://cloudar.be/?p=3681#comment-37</guid>

					<description><![CDATA[Good post. Thanks for the tip.]]></description>
			<content:encoded><![CDATA[<p>Good post. Thanks for the tip.</p>
]]></content:encoded>
		
			</item>
		<item>
		<title>
		By: Ben Bridts		</title>
		<link>https://cloudar.be/awsblog/using-the-application-load-balancer-and-waf-to-replace-cloudfront-security-groups/#comment-36</link>

		<dc:creator><![CDATA[Ben Bridts]]></dc:creator>
		<pubDate>Thu, 26 Sep 2019 13:46:24 +0000</pubDate>
		<guid isPermaLink="false">https://cloudar.be/?p=3681#comment-36</guid>

					<description><![CDATA[In reply to &lt;a href=&quot;https://cloudar.be/awsblog/using-the-application-load-balancer-and-waf-to-replace-cloudfront-security-groups/#comment-35&quot;&gt;Pritesh&lt;/a&gt;.

Hi Pritesh,

I just tested adding a regional WAF to a regional API Gateway and that works as expected:

Calling it wihtout a Header gives me a 403 Forbidden and with the Header allows my request through.

Are you sure you have the right default action configured in WAF?

Kind regards,
Ben]]></description>
			<content:encoded><![CDATA[<p>In reply to <a href="https://cloudar.be/awsblog/using-the-application-load-balancer-and-waf-to-replace-cloudfront-security-groups/#comment-35">Pritesh</a>.</p>
<p>Hi Pritesh,</p>
<p>I just tested adding a regional WAF to a regional API Gateway and that works as expected:</p>
<p>Calling it wihtout a Header gives me a 403 Forbidden and with the Header allows my request through.</p>
<p>Are you sure you have the right default action configured in WAF?</p>
<p>Kind regards,<br />
Ben</p>
]]></content:encoded>
		
			</item>
		<item>
		<title>
		By: Pritesh		</title>
		<link>https://cloudar.be/awsblog/using-the-application-load-balancer-and-waf-to-replace-cloudfront-security-groups/#comment-35</link>

		<dc:creator><![CDATA[Pritesh]]></dc:creator>
		<pubDate>Thu, 25 Jul 2019 10:31:14 +0000</pubDate>
		<guid isPermaLink="false">https://cloudar.be/?p=3681#comment-35</guid>

					<description><![CDATA[Thanks for the post.

I tried this appraoch for fronting a regional API Gateway with Cloudfront distribution. However, if I have different header values at the Cloudfront distribution and the API Gateway WAF ACL, it still allows the requests forwarded through Cloudfront.

Could you confirm if you&#039;re able to reproduce this issue?

Thanks!]]></description>
			<content:encoded><![CDATA[<p>Thanks for the post.</p>
<p>I tried this appraoch for fronting a regional API Gateway with Cloudfront distribution. However, if I have different header values at the Cloudfront distribution and the API Gateway WAF ACL, it still allows the requests forwarded through Cloudfront.</p>
<p>Could you confirm if you&#8217;re able to reproduce this issue?</p>
<p>Thanks!</p>
]]></content:encoded>
		
			</item>
		<item>
		<title>
		By: Bert		</title>
		<link>https://cloudar.be/awsblog/using-the-application-load-balancer-and-waf-to-replace-cloudfront-security-groups/#comment-34</link>

		<dc:creator><![CDATA[Bert]]></dc:creator>
		<pubDate>Wed, 23 Jan 2019 14:35:02 +0000</pubDate>
		<guid isPermaLink="false">https://cloudar.be/?p=3681#comment-34</guid>

					<description><![CDATA[In reply to &lt;a href=&quot;https://cloudar.be/awsblog/using-the-application-load-balancer-and-waf-to-replace-cloudfront-security-groups/#comment-33&quot;&gt;Narendra&lt;/a&gt;.

Hi Narendra,

It looks like you&#039;ve just pasted the unedited Lambda Function code from the CloudFormation Template provided by https://docs.aws.amazon.com/waf/latest/developerguide/tutorials-4xx-blocking.html

Could you show us something you&#039;ve already tried? I&#039;m afraid we can&#039;t just rewrite the whole thing for you, but we may be able to help you troubleshoot if you have specific questions or issues :)

Kind regards,
Bert]]></description>
			<content:encoded><![CDATA[<p>In reply to <a href="https://cloudar.be/awsblog/using-the-application-load-balancer-and-waf-to-replace-cloudfront-security-groups/#comment-33">Narendra</a>.</p>
<p>Hi Narendra,</p>
<p>It looks like you&#8217;ve just pasted the unedited Lambda Function code from the CloudFormation Template provided by <a href="https://docs.aws.amazon.com/waf/latest/developerguide/tutorials-4xx-blocking.html" rel="nofollow ugc">https://docs.aws.amazon.com/waf/latest/developerguide/tutorials-4xx-blocking.html</a></p>
<p>Could you show us something you&#8217;ve already tried? I&#8217;m afraid we can&#8217;t just rewrite the whole thing for you, but we may be able to help you troubleshoot if you have specific questions or issues 🙂</p>
<p>Kind regards,<br />
Bert</p>
]]></content:encoded>
		
			</item>
		<item>
		<title>
		By: Narendra		</title>
		<link>https://cloudar.be/awsblog/using-the-application-load-balancer-and-waf-to-replace-cloudfront-security-groups/#comment-33</link>

		<dc:creator><![CDATA[Narendra]]></dc:creator>
		<pubDate>Mon, 17 Dec 2018 13:54:22 +0000</pubDate>
		<guid isPermaLink="false">https://cloudar.be/?p=3681#comment-33</guid>

					<description><![CDATA[Hi All,

Currently i am implementing AWS WAF to block bad requests (4xx) automatically. but lambda function does not read elb s3 logs and path. cause lambda function written in python according to cloudfront. 

I also tried following aws tutorials. but it is integrated with cloudfront. while my site is not compatible with cloudfront. so i want to integrate lambda function (Bad Requests) from cloudfront to ALB. but lambda function unable to read alb logs and path.

https://docs.aws.amazon.com/waf/latest/developerguide/tutorials-4xx-blocking.html

Can some one help me to integrate or manipulate lambda function (Block Bad Requests) from cloudfront access log to alb access logs ( i mean lambda function should be read all access logs from alb logs instead of cloudfront logs.).

================lambda function (Block Bad Requests)===================

&#039;&#039;&#039;
Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
Licensed under the Amazon Software License (the &quot;License&quot;). You may not use this file except in compliance with the License.
A copy of the License is located at http://aws.amazon.com/asl/ or in the &quot;license&quot; file accompanying this file. 
This file is distributed on an &quot;AS IS&quot; BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, express or implied.
See the License for the specific language governing permissions and limitations under the License.
&#039;&#039;&#039;

import json
import urllib
import boto3
import gzip
import datetime
import time
import math

print(&#039;Loading function&#039;)

#======================================================================================================================
# Contants
#======================================================================================================================
# Configurables
OUTPUT_BUCKET = None
IP_SET_ID_MANUAL_BLOCK = None
IP_SET_ID_AUTO_BLOCK = None

BLACKLIST_BLOCK_PERIOD = None # in seconds
REQUEST_PER_MINUTE_LIMIT = None
BLOCK_ERROR_CODES = [&#039;400&#039;,&#039;403&#039;,&#039;404&#039;,&#039;405&#039;] # Error codes to parse logs for

LIMIT_IP_ADDRESS_RANGES_PER_IP_MATCH_CONDITION = 1000
API_CALL_NUM_RETRIES = 3

OUTPUT_FILE_NAME = &#039;current_outstanding_requesters.json&#039;

LINE_FORMAT = {
    &#039;date&#039;: 0,
    &#039;time&#039; : 1,
    &#039;source_ip&#039; : 4,
    &#039;code&#039; : 8
}

#======================================================================================================================
# Auxiliary Functions
#======================================================================================================================
def get_outstanding_requesters(bucket_name, key_name):
    print &#039;[get_outstanding_requesters] Start&#039;

    outstanding_requesters = {}
    outstanding_requesters[&#039;block&#039;] = {}
    result = {}
    num_requests = 0
    try:
        #--------------------------------------------------------------------------------------------------------------
        print &#039;[get_outstanding_requesters] \tDownload file from S3&#039;
        #--------------------------------------------------------------------------------------------------------------
        local_file_path = &#039;/tmp/&#039; + key_name.split(&#039;/&#039;)[-1]
        s3 = boto3.client(&#039;s3&#039;)
        s3.download_file(bucket_name, key_name, local_file_path)

        #--------------------------------------------------------------------------------------------------------------
        print &#039;[get_outstanding_requesters] \tRead file content&#039;
        #--------------------------------------------------------------------------------------------------------------
        with gzip.open(local_file_path,&#039;r&#039;) as content:
            for line in content:
                try:
                    if line.startswith(&#039;#&#039;):
                        continue

                    line_data = line.split(&#039;\t&#039;)
                    if line_data[LINE_FORMAT[&#039;code&#039;]] in BLOCK_ERROR_CODES:
                        request_key = line_data[LINE_FORMAT[&#039;date&#039;]]
                        request_key += &#039;-&#039; + line_data[LINE_FORMAT[&#039;time&#039;]][:-3]
                        request_key += &#039;-&#039; + line_data[LINE_FORMAT[&#039;source_ip&#039;]]
                        if request_key in result.keys():
                            result[request_key] += 1
                        else:
                            result[request_key] = 1

                    num_requests += 1

                except Exception, e:
                    print (&quot;[get_outstanding_requesters] \t\tError to process line: %s&quot;%line)

        #--------------------------------------------------------------------------------------------------------------
        print &#039;[get_outstanding_requesters] \tKeep only outstanding requesters&#039;
        #--------------------------------------------------------------------------------------------------------------
        now_timestamp_str = datetime.datetime.now().strftime(&quot;%Y-%m-%d %H:%M:%S&quot;)
        for k, v in result.iteritems():
            k = k.split(&#039;-&#039;)[-1]
            if v &#062; REQUEST_PER_MINUTE_LIMIT:
                if k not in outstanding_requesters[&#039;block&#039;].keys() or outstanding_requesters[&#039;block&#039;][k]  REQUEST_PER_MINUTE_LIMIT:
                if k in outstanding_requesters[&#039;block&#039;].keys():
                    print &quot;[merge_current_blocked_requesters] \t\tUpdating data of BLOCK %s rule&quot;%k
                    max_v = v[&#039;max_req_per_min&#039;]
                    if outstanding_requesters[&#039;block&#039;][k][&#039;max_req_per_min&#039;] &#062; max_v:
                        max_v = outstanding_requesters[&#039;block&#039;][k][&#039;max_req_per_min&#039;]
                    outstanding_requesters[&#039;block&#039;][k] = { &#039;max_req_per_min&#039;: max_v, &#039;updated_at&#039;: now_timestamp_str }
                else:
                    prev_updated_at = datetime.datetime.strptime(v[&#039;updated_at&#039;], &quot;%Y-%m-%d %H:%M:%S&quot;)
                    total_diff_sec = (now_timestamp - prev_updated_at).total_seconds()
                    if total_diff_sec &#062; (BLACKLIST_BLOCK_PERIOD):
                        print &quot;[merge_current_blocked_requesters] \t\tExpired BLOCK %s rule&quot;%k
                        outstanding_requesters[&#039;block&#039;][k] = v

    except Exception, e:
        print &quot;[merge_current_blocked_requesters] \tError merging data&quot;

    print &quot;[merge_current_blocked_requesters] End&quot;
    return outstanding_requesters

def write_output(key_name, outstanding_requesters):
    print &quot;[write_output] Start&quot;

    try:
        current_data = &#039;/tmp/&#039; + key_name.split(&#039;/&#039;)[-1] + &#039;_LOCAL.json&#039;
        with open(current_data, &#039;w&#039;) as outfile:
            json.dump(outstanding_requesters, outfile)

        s3 = boto3.client(&#039;s3&#039;)
        s3.upload_file(current_data, OUTPUT_BUCKET, OUTPUT_FILE_NAME, ExtraArgs={&#039;ContentType&#039;: &quot;application/json&quot;})

    except Exception, e:
        print &quot;[write_output] \tError to write output file&quot;

    print &quot;[write_output] End&quot;

def waf_get_ip_set(ip_set_id):
    response = None
    waf = boto3.client(&#039;waf&#039;)

    for attempt in range(API_CALL_NUM_RETRIES):
        try:
            response = waf.get_ip_set(IPSetId=ip_set_id)
        except Exception, e:
            print e
            delay = math.pow(2, attempt)
            print &quot;[waf_get_ip_set] Retrying in %d seconds...&quot; % (delay)
            time.sleep(delay)
        else:
            break
    else:
        print &quot;[waf_get_ip_set] Failed ALL attempts to call API&quot;

    return response

def waf_update_ip_set(ip_set_id, updates_list):
    response = None

    if updates_list != []:
        waf = boto3.client(&#039;waf&#039;)
        for attempt in range(API_CALL_NUM_RETRIES):
            try:
                response = waf.update_ip_set(IPSetId=ip_set_id,
                    ChangeToken=waf.get_change_token()[&#039;ChangeToken&#039;],
                    Updates=updates_list)
            except Exception, e:
                delay = math.pow(2, attempt)
                print &quot;[waf_update_ip_set] Retrying in %d seconds...&quot; % (delay)
                time.sleep(delay)
            else:
                break
        else:
            print &quot;[waf_update_ip_set] Failed ALL attempts to call API&quot;

    return response

def get_ip_set_already_blocked():
    print &quot;[get_ip_set_already_blocked] Start&quot;
    ip_set_already_blocked = []
    try:
        if IP_SET_ID_MANUAL_BLOCK != None:
            response = waf_get_ip_set(IP_SET_ID_MANUAL_BLOCK)
            if response != None:
                for k in response[&#039;IPSet&#039;][&#039;IPSetDescriptors&#039;]:
                    ip_set_already_blocked.append(k[&#039;Value&#039;])
    except Exception, e:
        print &quot;[get_ip_set_already_blocked] Error getting WAF IP Set&quot;
        print e

    print &quot;[get_ip_set_already_blocked] End&quot;
    return ip_set_already_blocked

def is_already_blocked(ip, ip_set):
    result = False

    try:
        for net in ip_set:
            ipaddr = int(&#039;&#039;.join([ &#039;%02x&#039; % int(x) for x in ip.split(&#039;.&#039;) ]), 16)
            netstr, bits = net.split(&#039;/&#039;)
            netaddr = int(&#039;&#039;.join([ &#039;%02x&#039; % int(x) for x in netstr.split(&#039;.&#039;) ]), 16)
            mask = (0xffffffff &#060;&#060; (32 - int(bits))) &#038; 0xffffffff

            if (ipaddr &#038; mask) == (netaddr &#038; mask):
                result = True
                break
    except Exception, e:
        pass

    return result

def update_waf_ip_set(outstanding_requesters, ip_set_id, ip_set_already_blocked):
    print &#034;[update_waf_ip_set] Start&#034;

    counter = 0
    try:
        if ip_set_id == None:
            print &#034;[update_waf_ip_set] Ignore process when ip_set_id is None&#034;
            return

        updates_list = []
        waf = boto3.client(&#039;waf&#039;)

        #--------------------------------------------------------------------------------------------------------------
        print &#034;[update_waf_ip_set] \tTruncate [if necessary] list to respect WAF limit&#034;
        #--------------------------------------------------------------------------------------------------------------
        top_outstanding_requesters = {}
        for key, value in sorted(outstanding_requesters.items(), key=lambda kv: kv[1][&#039;max_req_per_min&#039;], reverse=True):
            if counter &#060; LIMIT_IP_ADDRESS_RANGES_PER_IP_MATCH_CONDITION:
                if not is_already_blocked(key, ip_set_already_blocked):
                    top_outstanding_requesters[key] = value
                    counter += 1
            else:
                break

        #--------------------------------------------------------------------------------------------------------------
        print &#034;[update_waf_ip_set] \tRemove IPs that are not in current outstanding requesters list&#034;
        #--------------------------------------------------------------------------------------------------------------
        response = waf_get_ip_set(ip_set_id)
        if response != None:
            for k in response[&#039;IPSet&#039;][&#039;IPSetDescriptors&#039;]:
                ip_value = k[&#039;Value&#039;].split(&#039;/&#039;)[0]
                if ip_value not in top_outstanding_requesters.keys():
                    updates_list.append({
                        &#039;Action&#039;: &#039;DELETE&#039;,
                        &#039;IPSetDescriptor&#039;: {
                            &#039;Type&#039;: &#039;IPV4&#039;,
                            &#039;Value&#039;: k[&#039;Value&#039;]
                        }
                    })
                else:
                    # Dont block an already blocked IP
                    top_outstanding_requesters.pop(ip_value, None)

        #--------------------------------------------------------------------------------------------------------------
        print &#034;[update_waf_ip_set] \tBlock remaining outstanding requesters&#034;
        #--------------------------------------------------------------------------------------------------------------
        for k in top_outstanding_requesters.keys():
            updates_list.append({
                &#039;Action&#039;: &#039;INSERT&#039;,
                &#039;IPSetDescriptor&#039;: {
                    &#039;Type&#039;: &#039;IPV4&#039;,
                    &#039;Value&#039;: &#034;%s/32&#034;%k
                }
            })

        #--------------------------------------------------------------------------------------------------------------
        print &#034;[update_waf_ip_set] \tCommit changes in WAF IP set&#034;
        #--------------------------------------------------------------------------------------------------------------
        response = waf_update_ip_set(ip_set_id, updates_list)

    except Exception, e:
        print &#034;[update_waf_ip_set] Error to update waf ip set&#034;
        print e

    print &#034;[update_waf_ip_set] End&#034;
    return counter

#======================================================================================================================
# Lambda Entry Point
#======================================================================================================================
def lambda_handler(event, context):
    print &#039;[lambda_handler] Start&#039;
    bucket_name = event[&#039;Records&#039;][0][&#039;s3&#039;][&#039;bucket&#039;][&#039;name&#039;]
    key_name = urllib.unquote_plus(event[&#039;Records&#039;][0][&#039;s3&#039;][&#039;object&#039;][&#039;key&#039;]).decode(&#039;utf8&#039;)

    try:
        if key_name == OUTPUT_FILE_NAME:
            print &#039;[lambda_handler] \tIgnore processinf output file&#039;
            return

        #--------------------------------------------------------------------------------------------------------------
        print &#034;[lambda_handler] \tReading (if necessary) CloudFormation output values&#034;
        #--------------------------------------------------------------------------------------------------------------
        global OUTPUT_BUCKET
        global IP_SET_ID_MANUAL_BLOCK
        global IP_SET_ID_AUTO_BLOCK
        global BLACKLIST_BLOCK_PERIOD
        global REQUEST_PER_MINUTE_LIMIT

        if (OUTPUT_BUCKET == None or IP_SET_ID_MANUAL_BLOCK == None or
            IP_SET_ID_AUTO_BLOCK == None or BLACKLIST_BLOCK_PERIOD == None or
            REQUEST_PER_MINUTE_LIMIT == None):

            outputs = {}
            cf = boto3.client(&#039;cloudformation&#039;)
            stack_name = context.invoked_function_arn.split(&#039;:&#039;)[6].rsplit(&#039;-&#039;, 2)[0]
            response = cf.describe_stacks(StackName=stack_name)
            for e in response[&#039;Stacks&#039;][0][&#039;Outputs&#039;]:
                outputs[e[&#039;OutputKey&#039;]] = e[&#039;OutputValue&#039;]

            if OUTPUT_BUCKET == None:
                OUTPUT_BUCKET = outputs[&#039;CloudFrontAccessLogBucket&#039;]
            if IP_SET_ID_MANUAL_BLOCK == None:
                IP_SET_ID_MANUAL_BLOCK = outputs[&#039;ManualBlockIPSetID&#039;]
            if IP_SET_ID_AUTO_BLOCK == None:
                IP_SET_ID_AUTO_BLOCK = outputs[&#039;AutoBlockIPSetID&#039;]
            if BLACKLIST_BLOCK_PERIOD == None:
                BLACKLIST_BLOCK_PERIOD = int(outputs[&#039;WAFBlockPeriod&#039;]) # in seconds
            if REQUEST_PER_MINUTE_LIMIT == None:
                REQUEST_PER_MINUTE_LIMIT = int(outputs[&#039;RequestThreshold&#039;])

        print &#034;[lambda_handler] \t\tOUTPUT_BUCKET = %s&#034;%OUTPUT_BUCKET
        print &#034;[lambda_handler] \t\tIP_SET_ID_MANUAL_BLOCK = %s&#034;%IP_SET_ID_MANUAL_BLOCK
        print &#034;[lambda_handler] \t\tIP_SET_ID_AUTO_BLOCK = %s&#034;%IP_SET_ID_AUTO_BLOCK
        print &#034;[lambda_handler] \t\tBLACKLIST_BLOCK_PERIOD = %d&#034;%BLACKLIST_BLOCK_PERIOD
        print &#034;[lambda_handler] \t\tREQUEST_PER_MINUTE_LIMIT = %d&#034;%REQUEST_PER_MINUTE_LIMIT

        #--------------------------------------------------------------------------------------------------------------
        print &#034;[lambda_handler] \tReading input data and get outstanding requesters&#034;
        #--------------------------------------------------------------------------------------------------------------
        outstanding_requesters, num_requests = get_outstanding_requesters(bucket_name, key_name)

        #--------------------------------------------------------------------------------------------------------------
        print &#034;[lambda_handler] \tMerge with current blocked requesters&#034;
        #--------------------------------------------------------------------------------------------------------------
        outstanding_requesters = merge_current_blocked_requesters(key_name, outstanding_requesters)

        #--------------------------------------------------------------------------------------------------------------
        print &#034;[lambda_handler] \tUpdate new blocked requesters list to S3&#034;
        #--------------------------------------------------------------------------------------------------------------
        write_output(key_name, outstanding_requesters)

        #--------------------------------------------------------------------------------------------------------------
        print &#034;[lambda_handler] \tUpdate WAF IP Set&#034;
        #--------------------------------------------------------------------------------------------------------------
        ip_set_already_blocked = get_ip_set_already_blocked()
        num_blocked = update_waf_ip_set(outstanding_requesters[&#039;block&#039;], IP_SET_ID_AUTO_BLOCK, ip_set_already_blocked)
        
        cw = boto3.client(&#039;cloudwatch&#039;)
        response = cw.put_metric_data(
            Namespace=&#039;WAFReactiveBlacklist-%s&#039;%OUTPUT_BUCKET,
            MetricData=[
                {
                    &#039;MetricName&#039;: &#039;IPBlocked&#039;,
                    &#039;Timestamp&#039;: datetime.datetime.now(),
                    &#039;Value&#039;: num_blocked,
                    &#039;Unit&#039;: &#039;Count&#039;
                },
                {
                    &#039;MetricName&#039;: &#039;NumRequests&#039;,
                    &#039;Timestamp&#039;: datetime.datetime.now(),
                    &#039;Value&#039;: num_requests,
                    &#039;Unit&#039;: &#039;Count&#039;
                }
            ]
        )

        return outstanding_requesters
    except Exception as e:
        raise e
    print &#039;[main] End&#039;

========================================================================



Thanks
Narendra]]></description>
			<content:encoded><![CDATA[<p>Hi All,</p>
<p>Currently i am implementing AWS WAF to block bad requests (4xx) automatically. but lambda function does not read elb s3 logs and path. cause lambda function written in python according to cloudfront. </p>
<p>I also tried following aws tutorials. but it is integrated with cloudfront. while my site is not compatible with cloudfront. so i want to integrate lambda function (Bad Requests) from cloudfront to ALB. but lambda function unable to read alb logs and path.</p>
<p><a href="https://docs.aws.amazon.com/waf/latest/developerguide/tutorials-4xx-blocking.html" rel="nofollow ugc">https://docs.aws.amazon.com/waf/latest/developerguide/tutorials-4xx-blocking.html</a></p>
<p>Can some one help me to integrate or manipulate lambda function (Block Bad Requests) from cloudfront access log to alb access logs ( i mean lambda function should be read all access logs from alb logs instead of cloudfront logs.).</p>
<p>================lambda function (Block Bad Requests)===================</p>
<p>&#8221;&#8217;<br />
Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.<br />
Licensed under the Amazon Software License (the &#8220;License&#8221;). You may not use this file except in compliance with the License.<br />
A copy of the License is located at <a href="http://aws.amazon.com/asl/" rel="nofollow ugc">http://aws.amazon.com/asl/</a> or in the &#8220;license&#8221; file accompanying this file.<br />
This file is distributed on an &#8220;AS IS&#8221; BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, express or implied.<br />
See the License for the specific language governing permissions and limitations under the License.<br />
&#8221;&#8217;</p>
<p>import json<br />
import urllib<br />
import boto3<br />
import gzip<br />
import datetime<br />
import time<br />
import math</p>
<p>print(&#8216;Loading function&#8217;)</p>
<p>#======================================================================================================================<br />
# Contants<br />
#======================================================================================================================<br />
# Configurables<br />
OUTPUT_BUCKET = None<br />
IP_SET_ID_MANUAL_BLOCK = None<br />
IP_SET_ID_AUTO_BLOCK = None</p>
<p>BLACKLIST_BLOCK_PERIOD = None # in seconds<br />
REQUEST_PER_MINUTE_LIMIT = None<br />
BLOCK_ERROR_CODES = [&#8216;400&#8242;,&#8217;403&#8242;,&#8217;404&#8242;,&#8217;405&#8217;] # Error codes to parse logs for</p>
<p>LIMIT_IP_ADDRESS_RANGES_PER_IP_MATCH_CONDITION = 1000<br />
API_CALL_NUM_RETRIES = 3</p>
<p>OUTPUT_FILE_NAME = &#8216;current_outstanding_requesters.json&#8217;</p>
<p>LINE_FORMAT = {<br />
    &#8216;date&#8217;: 0,<br />
    &#8216;time&#8217; : 1,<br />
    &#8216;source_ip&#8217; : 4,<br />
    &#8216;code&#8217; : 8<br />
}</p>
<p>#======================================================================================================================<br />
# Auxiliary Functions<br />
#======================================================================================================================<br />
def get_outstanding_requesters(bucket_name, key_name):<br />
    print &#8216;[get_outstanding_requesters] Start&#8217;</p>
<p>    outstanding_requesters = {}<br />
    outstanding_requesters[&#8216;block&#8217;] = {}<br />
    result = {}<br />
    num_requests = 0<br />
    try:<br />
        #&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br />
        print &#8216;[get_outstanding_requesters] \tDownload file from S3&#8217;<br />
        #&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br />
        local_file_path = &#8216;/tmp/&#8217; + key_name.split(&#8216;/&#8217;)[-1]<br />
        s3 = boto3.client(&#8216;s3&#8217;)<br />
        s3.download_file(bucket_name, key_name, local_file_path)</p>
<p>        #&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br />
        print &#8216;[get_outstanding_requesters] \tRead file content&#8217;<br />
        #&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br />
        with gzip.open(local_file_path,&#8217;r&#8217;) as content:<br />
            for line in content:<br />
                try:<br />
                    if line.startswith(&#8216;#&#8217;):<br />
                        continue</p>
<p>                    line_data = line.split(&#8216;\t&#8217;)<br />
                    if line_data[LINE_FORMAT[&#8216;code&#8217;]] in BLOCK_ERROR_CODES:<br />
                        request_key = line_data[LINE_FORMAT[&#8216;date&#8217;]]<br />
                        request_key += &#8216;-&#8216; + line_data[LINE_FORMAT[&#8216;time&#8217;]][:-3]<br />
                        request_key += &#8216;-&#8216; + line_data[LINE_FORMAT[&#8216;source_ip&#8217;]]<br />
                        if request_key in result.keys():<br />
                            result[request_key] += 1<br />
                        else:<br />
                            result[request_key] = 1</p>
<p>                    num_requests += 1</p>
<p>                except Exception, e:<br />
                    print (&#8220;[get_outstanding_requesters] \t\tError to process line: %s&#8221;%line)</p>
<p>        #&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br />
        print &#8216;[get_outstanding_requesters] \tKeep only outstanding requesters&#8217;<br />
        #&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br />
        now_timestamp_str = datetime.datetime.now().strftime(&#8220;%Y-%m-%d %H:%M:%S&#8221;)<br />
        for k, v in result.iteritems():<br />
            k = k.split(&#8216;-&#8216;)[-1]<br />
            if v &gt; REQUEST_PER_MINUTE_LIMIT:<br />
                if k not in outstanding_requesters[&#8216;block&#8217;].keys() or outstanding_requesters[&#8216;block&#8217;][k]  REQUEST_PER_MINUTE_LIMIT:<br />
                if k in outstanding_requesters[&#8216;block&#8217;].keys():<br />
                    print &#8220;[merge_current_blocked_requesters] \t\tUpdating data of BLOCK %s rule&#8221;%k<br />
                    max_v = v[&#8216;max_req_per_min&#8217;]<br />
                    if outstanding_requesters[&#8216;block&#8217;][k][&#8216;max_req_per_min&#8217;] &gt; max_v:<br />
                        max_v = outstanding_requesters[&#8216;block&#8217;][k][&#8216;max_req_per_min&#8217;]<br />
                    outstanding_requesters[&#8216;block&#8217;][k] = { &#8216;max_req_per_min&#8217;: max_v, &#8216;updated_at&#8217;: now_timestamp_str }<br />
                else:<br />
                    prev_updated_at = datetime.datetime.strptime(v[&#8216;updated_at&#8217;], &#8220;%Y-%m-%d %H:%M:%S&#8221;)<br />
                    total_diff_sec = (now_timestamp &#8211; prev_updated_at).total_seconds()<br />
                    if total_diff_sec &gt; (BLACKLIST_BLOCK_PERIOD):<br />
                        print &#8220;[merge_current_blocked_requesters] \t\tExpired BLOCK %s rule&#8221;%k<br />
                        outstanding_requesters[&#8216;block&#8217;][k] = v</p>
<p>    except Exception, e:<br />
        print &#8220;[merge_current_blocked_requesters] \tError merging data&#8221;</p>
<p>    print &#8220;[merge_current_blocked_requesters] End&#8221;<br />
    return outstanding_requesters</p>
<p>def write_output(key_name, outstanding_requesters):<br />
    print &#8220;[write_output] Start&#8221;</p>
<p>    try:<br />
        current_data = &#8216;/tmp/&#8217; + key_name.split(&#8216;/&#8217;)[-1] + &#8216;_LOCAL.json&#8217;<br />
        with open(current_data, &#8216;w&#8217;) as outfile:<br />
            json.dump(outstanding_requesters, outfile)</p>
<p>        s3 = boto3.client(&#8216;s3&#8217;)<br />
        s3.upload_file(current_data, OUTPUT_BUCKET, OUTPUT_FILE_NAME, ExtraArgs={&#8216;ContentType&#8217;: &#8220;application/json&#8221;})</p>
<p>    except Exception, e:<br />
        print &#8220;[write_output] \tError to write output file&#8221;</p>
<p>    print &#8220;[write_output] End&#8221;</p>
<p>def waf_get_ip_set(ip_set_id):<br />
    response = None<br />
    waf = boto3.client(&#8216;waf&#8217;)</p>
<p>    for attempt in range(API_CALL_NUM_RETRIES):<br />
        try:<br />
            response = waf.get_ip_set(IPSetId=ip_set_id)<br />
        except Exception, e:<br />
            print e<br />
            delay = math.pow(2, attempt)<br />
            print &#8220;[waf_get_ip_set] Retrying in %d seconds&#8230;&#8221; % (delay)<br />
            time.sleep(delay)<br />
        else:<br />
            break<br />
    else:<br />
        print &#8220;[waf_get_ip_set] Failed ALL attempts to call API&#8221;</p>
<p>    return response</p>
<p>def waf_update_ip_set(ip_set_id, updates_list):<br />
    response = None</p>
<p>    if updates_list != []:<br />
        waf = boto3.client(&#8216;waf&#8217;)<br />
        for attempt in range(API_CALL_NUM_RETRIES):<br />
            try:<br />
                response = waf.update_ip_set(IPSetId=ip_set_id,<br />
                    ChangeToken=waf.get_change_token()[&#8216;ChangeToken&#8217;],<br />
                    Updates=updates_list)<br />
            except Exception, e:<br />
                delay = math.pow(2, attempt)<br />
                print &#8220;[waf_update_ip_set] Retrying in %d seconds&#8230;&#8221; % (delay)<br />
                time.sleep(delay)<br />
            else:<br />
                break<br />
        else:<br />
            print &#8220;[waf_update_ip_set] Failed ALL attempts to call API&#8221;</p>
<p>    return response</p>
<p>def get_ip_set_already_blocked():<br />
    print &#8220;[get_ip_set_already_blocked] Start&#8221;<br />
    ip_set_already_blocked = []<br />
    try:<br />
        if IP_SET_ID_MANUAL_BLOCK != None:<br />
            response = waf_get_ip_set(IP_SET_ID_MANUAL_BLOCK)<br />
            if response != None:<br />
                for k in response[&#8216;IPSet&#8217;][&#8216;IPSetDescriptors&#8217;]:<br />
                    ip_set_already_blocked.append(k[&#8216;Value&#8217;])<br />
    except Exception, e:<br />
        print &#8220;[get_ip_set_already_blocked] Error getting WAF IP Set&#8221;<br />
        print e</p>
<p>    print &#8220;[get_ip_set_already_blocked] End&#8221;<br />
    return ip_set_already_blocked</p>
<p>def is_already_blocked(ip, ip_set):<br />
    result = False</p>
<p>    try:<br />
        for net in ip_set:<br />
            ipaddr = int(&#8221;.join([ &#8216;%02x&#8217; % int(x) for x in ip.split(&#8216;.&#8217;) ]), 16)<br />
            netstr, bits = net.split(&#8216;/&#8217;)<br />
            netaddr = int(&#8221;.join([ &#8216;%02x&#8217; % int(x) for x in netstr.split(&#8216;.&#8217;) ]), 16)<br />
            mask = (0xffffffff &lt;&lt; (32 &#8211; int(bits))) &amp; 0xffffffff</p>
<p>            if (ipaddr &amp; mask) == (netaddr &amp; mask):<br />
                result = True<br />
                break<br />
    except Exception, e:<br />
        pass</p>
<p>    return result</p>
<p>def update_waf_ip_set(outstanding_requesters, ip_set_id, ip_set_already_blocked):<br />
    print &quot;[update_waf_ip_set] Start&quot;</p>
<p>    counter = 0<br />
    try:<br />
        if ip_set_id == None:<br />
            print &quot;[update_waf_ip_set] Ignore process when ip_set_id is None&quot;<br />
            return</p>
<p>        updates_list = []<br />
        waf = boto3.client(&#039;waf&#039;)</p>
<p>        #&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br />
        print &quot;[update_waf_ip_set] \tTruncate [if necessary] list to respect WAF limit&quot;<br />
        #&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br />
        top_outstanding_requesters = {}<br />
        for key, value in sorted(outstanding_requesters.items(), key=lambda kv: kv[1][&#039;max_req_per_min&#039;], reverse=True):<br />
            if counter &lt; LIMIT_IP_ADDRESS_RANGES_PER_IP_MATCH_CONDITION:<br />
                if not is_already_blocked(key, ip_set_already_blocked):<br />
                    top_outstanding_requesters[key] = value<br />
                    counter += 1<br />
            else:<br />
                break</p>
<p>        #&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br />
        print &quot;[update_waf_ip_set] \tRemove IPs that are not in current outstanding requesters list&quot;<br />
        #&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br />
        response = waf_get_ip_set(ip_set_id)<br />
        if response != None:<br />
            for k in response[&#039;IPSet&#039;][&#039;IPSetDescriptors&#039;]:<br />
                ip_value = k[&#039;Value&#039;].split(&#039;/&#039;)[0]<br />
                if ip_value not in top_outstanding_requesters.keys():<br />
                    updates_list.append({<br />
                        &#039;Action&#039;: &#039;DELETE&#039;,<br />
                        &#039;IPSetDescriptor&#039;: {<br />
                            &#039;Type&#039;: &#039;IPV4&#039;,<br />
                            &#039;Value&#039;: k[&#039;Value&#039;]<br />
                        }<br />
                    })<br />
                else:<br />
                    # Dont block an already blocked IP<br />
                    top_outstanding_requesters.pop(ip_value, None)</p>
<p>        #&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br />
        print &quot;[update_waf_ip_set] \tBlock remaining outstanding requesters&quot;<br />
        #&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br />
        for k in top_outstanding_requesters.keys():<br />
            updates_list.append({<br />
                &#039;Action&#039;: &#039;INSERT&#039;,<br />
                &#039;IPSetDescriptor&#039;: {<br />
                    &#039;Type&#039;: &#039;IPV4&#039;,<br />
                    &#039;Value&#039;: &quot;%s/32&quot;%k<br />
                }<br />
            })</p>
<p>        #&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br />
        print &quot;[update_waf_ip_set] \tCommit changes in WAF IP set&quot;<br />
        #&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br />
        response = waf_update_ip_set(ip_set_id, updates_list)</p>
<p>    except Exception, e:<br />
        print &quot;[update_waf_ip_set] Error to update waf ip set&quot;<br />
        print e</p>
<p>    print &quot;[update_waf_ip_set] End&quot;<br />
    return counter</p>
<p>#======================================================================================================================<br />
# Lambda Entry Point<br />
#======================================================================================================================<br />
def lambda_handler(event, context):<br />
    print &#039;[lambda_handler] Start&#039;<br />
    bucket_name = event[&#039;Records&#039;][0][&#039;s3&#039;][&#039;bucket&#039;][&#039;name&#039;]<br />
    key_name = urllib.unquote_plus(event[&#039;Records&#039;][0][&#039;s3&#039;][&#039;object&#039;][&#039;key&#039;]).decode(&#039;utf8&#039;)</p>
<p>    try:<br />
        if key_name == OUTPUT_FILE_NAME:<br />
            print &#039;[lambda_handler] \tIgnore processinf output file&#039;<br />
            return</p>
<p>        #&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br />
        print &quot;[lambda_handler] \tReading (if necessary) CloudFormation output values&quot;<br />
        #&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br />
        global OUTPUT_BUCKET<br />
        global IP_SET_ID_MANUAL_BLOCK<br />
        global IP_SET_ID_AUTO_BLOCK<br />
        global BLACKLIST_BLOCK_PERIOD<br />
        global REQUEST_PER_MINUTE_LIMIT</p>
<p>        if (OUTPUT_BUCKET == None or IP_SET_ID_MANUAL_BLOCK == None or<br />
            IP_SET_ID_AUTO_BLOCK == None or BLACKLIST_BLOCK_PERIOD == None or<br />
            REQUEST_PER_MINUTE_LIMIT == None):</p>
<p>            outputs = {}<br />
            cf = boto3.client(&#039;cloudformation&#039;)<br />
            stack_name = context.invoked_function_arn.split(&#039;:&#039;)[6].rsplit(&#039;-&#039;, 2)[0]<br />
            response = cf.describe_stacks(StackName=stack_name)<br />
            for e in response[&#039;Stacks&#039;][0][&#039;Outputs&#039;]:<br />
                outputs[e[&#039;OutputKey&#039;]] = e[&#039;OutputValue&#039;]</p>
<p>            if OUTPUT_BUCKET == None:<br />
                OUTPUT_BUCKET = outputs[&#039;CloudFrontAccessLogBucket&#039;]<br />
            if IP_SET_ID_MANUAL_BLOCK == None:<br />
                IP_SET_ID_MANUAL_BLOCK = outputs[&#039;ManualBlockIPSetID&#039;]<br />
            if IP_SET_ID_AUTO_BLOCK == None:<br />
                IP_SET_ID_AUTO_BLOCK = outputs[&#039;AutoBlockIPSetID&#039;]<br />
            if BLACKLIST_BLOCK_PERIOD == None:<br />
                BLACKLIST_BLOCK_PERIOD = int(outputs[&#039;WAFBlockPeriod&#039;]) # in seconds<br />
            if REQUEST_PER_MINUTE_LIMIT == None:<br />
                REQUEST_PER_MINUTE_LIMIT = int(outputs[&#039;RequestThreshold&#039;])</p>
<p>        print &quot;[lambda_handler] \t\tOUTPUT_BUCKET = %s&quot;%OUTPUT_BUCKET<br />
        print &quot;[lambda_handler] \t\tIP_SET_ID_MANUAL_BLOCK = %s&quot;%IP_SET_ID_MANUAL_BLOCK<br />
        print &quot;[lambda_handler] \t\tIP_SET_ID_AUTO_BLOCK = %s&quot;%IP_SET_ID_AUTO_BLOCK<br />
        print &quot;[lambda_handler] \t\tBLACKLIST_BLOCK_PERIOD = %d&quot;%BLACKLIST_BLOCK_PERIOD<br />
        print &quot;[lambda_handler] \t\tREQUEST_PER_MINUTE_LIMIT = %d&quot;%REQUEST_PER_MINUTE_LIMIT</p>
<p>        #&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br />
        print &quot;[lambda_handler] \tReading input data and get outstanding requesters&quot;<br />
        #&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br />
        outstanding_requesters, num_requests = get_outstanding_requesters(bucket_name, key_name)</p>
<p>        #&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br />
        print &quot;[lambda_handler] \tMerge with current blocked requesters&quot;<br />
        #&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br />
        outstanding_requesters = merge_current_blocked_requesters(key_name, outstanding_requesters)</p>
<p>        #&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br />
        print &quot;[lambda_handler] \tUpdate new blocked requesters list to S3&quot;<br />
        #&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br />
        write_output(key_name, outstanding_requesters)</p>
<p>        #&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br />
        print &quot;[lambda_handler] \tUpdate WAF IP Set&quot;<br />
        #&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br />
        ip_set_already_blocked = get_ip_set_already_blocked()<br />
        num_blocked = update_waf_ip_set(outstanding_requesters[&#039;block&#039;], IP_SET_ID_AUTO_BLOCK, ip_set_already_blocked)</p>
<p>        cw = boto3.client(&#039;cloudwatch&#039;)<br />
        response = cw.put_metric_data(<br />
            Namespace=&#039;WAFReactiveBlacklist-%s&#039;%OUTPUT_BUCKET,<br />
            MetricData=[<br />
                {<br />
                    &#039;MetricName&#039;: &#039;IPBlocked&#039;,<br />
                    &#039;Timestamp&#039;: datetime.datetime.now(),<br />
                    &#039;Value&#039;: num_blocked,<br />
                    &#039;Unit&#039;: &#039;Count&#039;<br />
                },<br />
                {<br />
                    &#039;MetricName&#039;: &#039;NumRequests&#039;,<br />
                    &#039;Timestamp&#039;: datetime.datetime.now(),<br />
                    &#039;Value&#039;: num_requests,<br />
                    &#039;Unit&#039;: &#039;Count&#039;<br />
                }<br />
            ]<br />
        )</p>
<p>        return outstanding_requesters<br />
    except Exception as e:<br />
        raise e<br />
    print &#039;[main] End&#039;</p>
<p>========================================================================</p>
<p>Thanks<br />
Narendra</p>
]]></content:encoded>
		
			</item>
	</channel>
</rss>
