def update_nacl()

in lambda/guardduty_to_acl_lambda.py [0:0]


def update_nacl(netacl_id, host_ip, region):
    logger.info("log -- gd2acl entering update_nacl, netacl_id=%s, host_ip=%s" % (netacl_id, host_ip))

    ddb = boto3.resource('dynamodb')
    table = ddb.Table(ACLMETATABLE)

    hostipexists = table.query(
        KeyConditionExpression=Key('NetACLId').eq(netacl_id),
        FilterExpression=Attr('HostIp').eq(host_ip)
    )

    # Is HostIp already in table?
    if len(hostipexists['Items']) > 0:
        logger.info("log -- host IP %s already in table... exiting update." % (host_ip))
        return False

    else:

        # Get current NACL entries in DDB
        response = table.query(
            KeyConditionExpression=Key('NetACLId').eq(netacl_id)
        )

        # Get all the entries for NACL
        naclentries = response['Items']

        # Find oldest rule and available rule numbers from 71-80
        if naclentries:
            rulecount = response['Count']
            rulerange = list(range(71, 81))

            ddbrulerange = []
            naclrulerange = get_nacl_rules(netacl_id)

            for i in naclentries:
                ddbrulerange.append(int(i['RuleNo']))

            # Check state and exit if NACL rule not in sync with DDB
            ddbrulerange.sort()
            naclrulerange.sort()
            synccheck = set(naclrulerange).symmetric_difference(ddbrulerange)

            if ddbrulerange != naclrulerange:
                logger.info("log -- current DDB entries, %s." % (ddbrulerange))
                logger.info("log -- current NACL entries, %s." % (naclrulerange))
                logger.error('NACL rule state mismatch, %s exiting' % (sorted(synccheck)))
                exit()

            # Determine the NACL rule number and create rule
            if rulecount < 10:
                # Get the lowest rule number available in the range
                newruleno = min([x for x in rulerange if not x in naclrulerange])

                # Create new NACL rule, IP set entries and DDB state entry
                logger.info("log -- adding new rule %s, HostIP %s, to NACL %s." % (newruleno, host_ip, netacl_id))
                create_netacl_rule(netacl_id=netacl_id, host_ip=host_ip, rule_no=newruleno)
                create_ddb_rule(netacl_id=netacl_id, host_ip=host_ip, rule_no=newruleno, region=region)
                logger.info("log -- all possible NACL rule numbers, %s." % (rulerange))
                logger.info("log -- current DDB entries, %s." % (ddbrulerange))
                logger.info("log -- current NACL entries, %s." % (naclrulerange))
                logger.info("log -- new rule number, %s." % (newruleno))
                logger.info("log -- rule count for NACL %s is %s." % (netacl_id, int(rulecount) + 1))
                return True

            if rulecount >= 10:
                # Get oldest entry in DynamoDB table
                oldestrule = table.query(
                    KeyConditionExpression=Key('NetACLId').eq(netacl_id),
                    ScanIndexForward=True, # true = ascending, false = descending
                    Limit=1,
                )

                oldruleno = int((oldestrule)['Items'][0]['RuleNo'])
                oldrulets = int((oldestrule)['Items'][0]['CreatedAt'])
                oldhostip = oldestrule['Items'][0]['HostIp']
                newruleno = oldruleno

                # Delete old NACL rule and DDB state entry
                logger.info("log -- deleting current rule %s for IP %s from NACL %s." % (oldruleno, oldhostip, netacl_id))
                delete_netacl_rule(netacl_id=netacl_id, rule_no=oldruleno)
                delete_ddb_rule(netacl_id=netacl_id, created_at=oldrulets)

                # check if IP is also recorded in a fresh finding, don't remove IP from blacklist in that case
                response_nonexpired = table.scan( FilterExpression=Attr('CreatedAt').gt(oldrulets) & Attr('HostIp').eq(host_ip) )

                # Create new NACL rule, IP set entries and DDB state entry
                logger.info("log -- adding new rule %s, HostIP %s, to NACL %s." % (newruleno, host_ip, netacl_id))
                create_netacl_rule(netacl_id=netacl_id, host_ip=host_ip, rule_no=newruleno)
                create_ddb_rule(netacl_id=netacl_id, host_ip=host_ip, rule_no=newruleno, region=region)                
                logger.info("log -- all possible NACL rule numbers, %s." % (rulerange))
                logger.info("log -- current DDB entries, %s." % (ddbrulerange))
                logger.info("log -- current NACL entries, %s." % (naclrulerange))
                logger.info("log -- rule count for NACL %s is %s." % (netacl_id, int(rulecount)))
                return True

        else:
            # No entries in DDB Table start from 71
            naclrulerange = get_nacl_rules(netacl_id)
            newruleno = 71
            oldruleno = []
            rulecount = 0
            naclrulerange.sort()

            # Error and exit if NACL rules already present
            if naclrulerange:
                logger.error("log -- NACL %s, has existing entries, %s." % (netacl_id, naclrulerange))
                raise RuntimeError("NACL has existing entries.")

            # Create new NACL rule, IP set entries and DDB state entry
            logger.info("log -- adding new rule %s, HostIP %s, to NACL %s." % (newruleno, host_ip, netacl_id))
            create_netacl_rule(netacl_id=netacl_id, host_ip=host_ip, rule_no=newruleno)
            create_ddb_rule(netacl_id=netacl_id, host_ip=host_ip, rule_no=newruleno, region=region)
            logger.info("log -- rule count for NACL %s is %s." % (netacl_id, int(rulecount) + 1))
            return True

        if response['ResponseMetadata']['HTTPStatusCode'] == 200:
            return True
        else:
            return False