diff --git a/services/ec2/Ec2.py b/services/ec2/Ec2.py index 10d4ba1..cb64ea1 100644 --- a/services/ec2/Ec2.py +++ b/services/ec2/Ec2.py @@ -20,6 +20,8 @@ from services.ec2.drivers.Ec2ElbClassic import Ec2ElbClassic from services.ec2.drivers.Ec2AutoScaling import Ec2AutoScaling from services.ec2.drivers.Ec2EbsSnapshot import Ec2EbsSnapshot +from services.ec2.drivers.Ec2Vpc import Ec2Vpc +from services.ec2.drivers.Ec2NACL import Ec2NACL class Ec2(Service): def __init__(self, region): @@ -289,6 +291,49 @@ def getDefaultSG(self): finalArr.append(defaultSGs[i]) return finalArr + + def getVpcs(self): + filters = [] + if self.tags is not None: + filters = self.tags + + result = self.ec2Client.describe_vpcs( + Filters = filters + ) + + vpcList = result.get('Vpcs') + while result.get('NextToken') is not None: + result = self.ec2Client.describe_vpcs( + Filters = filters, + NextToken = result.get('NextToken') + ) + vpcList = vpcList + result.get('Vpcs') + + return vpcList + + def getFlowLogs(self): + ## No filter check in flow logs because the filter should be applied on VPC level + result = self.ec2Client.describe_flow_logs() + + flowLogList = result.get('FlowLogs') + while result.get('NextToken') is not None: + result = self.ec2Client.describe_flow_logs( + NextToken = result.get('NextToken') + ) + flowLogList = flowLogList + result.get('FlowLogs') + + return flowLogList + + def getNetworkACLs(self): + result = self.ec2Client.describe_network_acls() + + networkACLs = result.get('NetworkAcls') + while result.get('NextToken') is not None: + result = self.ec2Client.describe_network_acls( + NextToken = result.get('NextToken') + ) + networkACLs = networkACLs + result.get('NetworkAcls') + return networkACLs def advise(self): objs = {} @@ -415,5 +460,23 @@ def advise(self): obj = Ec2EIP(eip) obj.run(self.__class__) objs[f"ElasticIP::{eip['AllocationId']}"] = obj.getInfo() + + # VPC Checks + vpcs = self.getVpcs() + flowLogs = self.getFlowLogs() + for vpc in vpcs: + print(f"... (VPC::Virtual Private Cloud) inspecting {vpc['VpcId']}") + obj = Ec2Vpc(vpc, flowLogs, self.ec2Client) + obj.run(self.__class__) + objs[f"VPC::{vpc['VpcId']}"] = obj.getInfo() + + # NACL Checks + nacls = self.getNetworkACLs() + for nacl in nacls: + print(f"... (NACL::Network ACL) inspecting {nacl['NetworkAclId']}") + obj = Ec2NACL(nacl, self.ec2Client) + obj.run(self.__class__) + objs[f"NACL::{nacl['NetworkAclId']}"] = obj.getInfo() + return objs \ No newline at end of file diff --git a/services/ec2/drivers/Ec2NACL.py b/services/ec2/drivers/Ec2NACL.py new file mode 100644 index 0000000..8daae45 --- /dev/null +++ b/services/ec2/drivers/Ec2NACL.py @@ -0,0 +1,33 @@ +import boto3 +import botocore + +from services.Evaluator import Evaluator + +class Ec2NACL(Evaluator): + def __init__(self, nacl, ec2Client): + super().__init__() + self.nacl = nacl + self.ec2Client = ec2Client + self.init() + return + + def _checkNACLAssociation(self): + if not self.nacl['Associations']: + self.results['NACLAssociated'] = [-1, self.nacl['NetworkAclId']] + + return + + def _checkNACLIngressSensitivePort(self): + sensitivePort = [22, 3389] + for entry in self.nacl['Entries']: + if entry['RuleAction'] == 'allow' and entry['Egress'] == False: + if ('CidrBlock' in entry and entry['CidrBlock'] == '0.0.0.0/0') or ('Ipv6CidrBlock' in entry and entry['Ipv6CidrBlock'] == '::/0'): + if 'PortRange' in entry: + portFrom = entry['PortRange']['From'] + portTo = entry['PortRange']['To'] + for port in sensitivePort: + if portFrom <= port and portTo >= port: + self.results['NACLSensitivePort'] = [-1, self.nacl['NetworkAclId']] + return + + return \ No newline at end of file diff --git a/services/ec2/drivers/Ec2Vpc.py b/services/ec2/drivers/Ec2Vpc.py new file mode 100644 index 0000000..4fd0e08 --- /dev/null +++ b/services/ec2/drivers/Ec2Vpc.py @@ -0,0 +1,24 @@ +import boto3 +import botocore + +from services.Evaluator import Evaluator + +class Ec2Vpc(Evaluator): + def __init__(self, vpc, flowLogs, ec2Client): + super().__init__() + self.vpc = vpc + self.flowLogs = flowLogs + self.ec2Client = ec2Client + self.init() + return + + def _checkVpcFlowLogEnabled(self): + vpcId = self.vpc['VpcId'] + for flowLog in self.flowLogs: + if flowLog['ResourceId'] == vpcId and flowLog['TrafficType'] != 'ACCEPT': + return + + self.results['VPCFlowLogEnabled'] = [-1, self.vpc['VpcId']] + return + + \ No newline at end of file diff --git a/services/ec2/ec2.reporter.json b/services/ec2/ec2.reporter.json index b524e98..87812cf 100644 --- a/services/ec2/ec2.reporter.json +++ b/services/ec2/ec2.reporter.json @@ -637,5 +637,44 @@ "[RDS SQL Server]", "[endoflife]" ] + }, + "VPCFlowLogEnabled": { + "category": "S", + "^description": "You have {$COUNT} VPC has not enable VPC Flow Log. VPC Flow Log provide visibility into network traffic that traverses the VPC.", + "downtime": 0, + "slowness": 0, + "additionalCost": 1, + "criticality": "H", + "needFullTest": 0, + "shortDesc": "Enable VPC Flow Log", + "ref": [ + "[Amazon Elastic Compute Cloud controls]" + ] + }, + "NACLAssociated": { + "category": "O", + "^description": "You have {$COUNT} Network ACL has no subnet association. Remove unused Network ACL to improve operation efficiency.", + "downtime": 0, + "slowness": 0, + "additionalCost": 0, + "criticality": "L", + "needFullTest": 0, + "shortDesc": "Remove unused Network ACL", + "ref": [ + "[Control traffic to subnets using network ACLs]" + ] + }, + "NACLSensitivePort": { + "category": "S", + "^description": "You have {$COUNT} Network ACL has unrestricted ingress access to SSH/RDP port. Remove ingress access for the sensitive port", + "downtime": 0, + "slowness": 0, + "additionalCost": 0, + "criticality": "H", + "needFullTest": 0, + "shortDesc": "Remove unrestricted ingress access to sensitive port", + "ref": [ + "[Amazon Elastic Compute Cloud controls]" + ] } } \ No newline at end of file diff --git a/utils/Config.py b/utils/Config.py index 5135c5f..72d0a0b 100644 --- a/utils/Config.py +++ b/utils/Config.py @@ -57,6 +57,8 @@ class Config: 'ec2elbcommon': ['DICT', 'elb', 'LoadBalancerArn'], 'ec2instance': ['DICT', 'ec2InstanceData', 'InstanceId'], 'ec2secgroup': ['DICT', 'secGroup', 'GroupId'], + 'ec2vpc': ['DICT', 'vpc', 'VpcId'], + 'ec2nacl': ['DICT', 'nacl', 'NetworkAclId'], 'efsdriver': ['DICT', 'efs', 'FileSystemId'], 'ekscommon': ['ATTR', 'cluster'], 'elasticachememcached': ['DICT', 'cluster', 'ARN'],