-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(aws): add DirectConnect service and checks (#5522)
- Loading branch information
Showing
12 changed files
with
924 additions
and
0 deletions.
There are no files selected for viewing
Empty file.
6 changes: 6 additions & 0 deletions
6
prowler/providers/aws/services/directconnect/directconnect_client.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
from prowler.providers.aws.services.directconnect.directconnect_service import ( | ||
DirectConnect, | ||
) | ||
from prowler.providers.common.provider import Provider | ||
|
||
directconnect_client = DirectConnect(Provider.get_global_provider()) |
Empty file.
34 changes: 34 additions & 0 deletions
34
...ect/directconnect_connection_redundancy/directconnect_connection_redundancy.metadata.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
{ | ||
"Provider": "aws", | ||
"CheckID": "directconnect_connection_redundancy", | ||
"CheckTitle": "Ensure Direct Connect connections are redundant", | ||
"CheckType": [ | ||
"Resilience" | ||
], | ||
"ServiceName": "directconnect", | ||
"SubServiceName": "", | ||
"ResourceIdTemplate": "arn:partition:directconnect:region:account-id:directconnect/resource-id", | ||
"Severity": "medium", | ||
"ResourceType": "", | ||
"Description": "Checks the resilience of the AWS Direct Connect used to connect your on-premises.", | ||
"Risk": "This check alerts you if any Direct Connect connections are not redundant and the connections are coming from two distinct Direct Connect locations. Lack of location resiliency can result in unexpected downtime during maintenance, a fiber cut, a device failure, or a complete location failure.", | ||
"RelatedUrl": "https://docs.aws.amazon.com/awssupport/latest/user/fault-tolerance-checks.html#amazon-direct-connect-location-resiliency", | ||
"Remediation": { | ||
"Code": { | ||
"CLI": "", | ||
"NativeIaC": "", | ||
"Other": "", | ||
"Terraform": "" | ||
}, | ||
"Recommendation": { | ||
"Text": "To build Direct Connect location resiliency, you should have at least two connections from at least two distinct Direct Connect locations.", | ||
"Url": "https://aws.amazon.com/directconnect/resiliency-recommendation/" | ||
} | ||
}, | ||
"Categories": [ | ||
"redundancy" | ||
], | ||
"DependsOn": [], | ||
"RelatedTo": [], | ||
"Notes": "" | ||
} |
42 changes: 42 additions & 0 deletions
42
.../directconnect/directconnect_connection_redundancy/directconnect_connection_redundancy.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
from prowler.lib.check.models import Check, Check_Report_AWS | ||
from prowler.providers.aws.services.directconnect.directconnect_client import ( | ||
directconnect_client, | ||
) | ||
|
||
|
||
class directconnect_connection_redundancy(Check): | ||
def execute(self): | ||
findings = [] | ||
if len(directconnect_client.connections): | ||
regions = {} | ||
for conn in directconnect_client.connections.values(): | ||
if conn.region not in regions: | ||
regions[conn.region] = {} | ||
regions[conn.region]["Connections"] = 0 | ||
regions[conn.region]["Locations"] = set() | ||
regions[conn.region]["Connections"] += 1 | ||
regions[conn.region]["Locations"].add(conn.location) | ||
|
||
for region, connections in regions.items(): | ||
report = Check_Report_AWS(self.metadata()) | ||
report.region = region | ||
report.resource_arn = directconnect_client.audited_account_arn | ||
report.resource_id = directconnect_client.audited_account | ||
if connections["Connections"] == 1: | ||
report.status = "FAIL" | ||
report.status_extended = ( | ||
"There is only one Direct Connect connection." | ||
) | ||
else: # Connection Redundancy is met. | ||
if ( | ||
len(connections["Locations"]) == 1 | ||
): # All connections use the same location | ||
report.status = "FAIL" | ||
report.status_extended = f"There is only one location {next(iter(connections['Locations']))} used by all the Direct Connect connections." | ||
else: # Connection Redundancy and Location Redundancy is also met | ||
report.status = "PASS" | ||
report.status_extended = f"There are {connections['Connections']} Direct Connect connections across {len(connections['Locations'])} locations." | ||
|
||
findings.append(report) | ||
|
||
return findings |
149 changes: 149 additions & 0 deletions
149
prowler/providers/aws/services/directconnect/directconnect_service.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,149 @@ | ||
from typing import Optional | ||
|
||
from botocore.exceptions import ClientError | ||
from pydantic import BaseModel | ||
|
||
from prowler.lib.logger import logger | ||
from prowler.lib.scan_filters.scan_filters import is_resource_filtered | ||
from prowler.providers.aws.lib.service.service import AWSService | ||
|
||
|
||
class DirectConnect(AWSService): | ||
def __init__(self, provider): | ||
super().__init__(__class__.__name__, provider) | ||
self.connections = {} | ||
self.vifs = {} | ||
self.vgws = {} | ||
self.dxgws = {} | ||
self.__threading_call__(self._describe_connections) | ||
self.__threading_call__(self._describe_vifs) | ||
|
||
def _describe_connections(self, regional_client): | ||
"""List DirectConnect(s) in the given region. | ||
Args: | ||
regional_client: The regional AWS client. | ||
""" | ||
|
||
try: | ||
logger.info("DirectConnect - Listing Connections...") | ||
dx_connect = regional_client.describe_connections() | ||
|
||
for connection in dx_connect["connections"]: | ||
connection_arn = f"arn:{self.audited_partition}:directconnect:{regional_client.region}:{self.audited_account}:dxcon/{connection['connectionId']}" | ||
if not self.audit_resources or ( | ||
is_resource_filtered(connection_arn, self.audit_resources) | ||
): | ||
self.connections[connection_arn] = Connection( | ||
arn=connection_arn, | ||
id=connection["connectionId"], | ||
name=connection["connectionName"], | ||
location=connection["location"], | ||
region=regional_client.region, | ||
) | ||
|
||
except Exception as error: | ||
logger.error( | ||
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" | ||
) | ||
|
||
def _describe_vifs(self, regional_client): | ||
"""Describe each DirectConnect VIFs.""" | ||
|
||
logger.info("DirectConnect - Describing VIFs...") | ||
try: | ||
describe_vifs = regional_client.describe_virtual_interfaces() | ||
for vif in describe_vifs["virtualInterfaces"]: | ||
vif_id = vif["virtualInterfaceId"] | ||
vif_arn = f"arn:{self.audited_partition}:directconnect:{regional_client.region}:{self.audited_account}:dxvif/{vif_id}" | ||
if not self.audit_resources or ( | ||
is_resource_filtered(vif_arn, self.audit_resources) | ||
): | ||
vgw_id = vif.get("virtualGatewayId") | ||
connection_id = vif.get("connectionId") | ||
dxgw_id = vif.get("directConnectGatewayId") | ||
self.vifs[vif_arn] = VirtualInterface( | ||
arn=vif_arn, | ||
id=vif_id, | ||
name=vif["virtualInterfaceName"], | ||
connection_id=connection_id, | ||
vgw_gateway_id=vif["virtualGatewayId"], | ||
dx_gateway_id=dxgw_id, | ||
location=vif["location"], | ||
region=regional_client.region, | ||
) | ||
if vgw_id: | ||
vgw_arn = f"arn:{self.audited_partition}:directconnect:{regional_client.region}:{self.audited_account}:virtual-gateway/{vgw_id}" | ||
if vgw_arn in self.vgws: | ||
self.vgws[vgw_arn].vifs.append(vif_id) | ||
self.vgws[vgw_arn].connections.append(connection_id) | ||
else: | ||
self.vgws[vgw_arn] = VirtualGateway( | ||
arn=vgw_arn, | ||
id=vgw_id, | ||
vifs=[vif_id], | ||
connections=[connection_id], | ||
region=regional_client.region, | ||
) | ||
|
||
if dxgw_id: | ||
dxgw_arn = f"arn:{self.audited_partition}:directconnect:{regional_client.region}:{self.audited_account}:dx-gateway/{dxgw_id}" | ||
if dxgw_arn in self.dxgws: | ||
self.dxgws[dxgw_arn].vifs.append(vif_id) | ||
self.dxgws[dxgw_arn].connections.append(connection_id) | ||
else: | ||
self.dxgws[dxgw_arn] = DXGateway( | ||
arn=dxgw_arn, | ||
id=dxgw_id, | ||
vifs=[vif_id], | ||
connections=[connection_id], | ||
region=regional_client.region, | ||
) | ||
except ClientError as error: | ||
if error.response["Error"]["Code"] == "ResourceNotFoundException": | ||
logger.warning( | ||
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" | ||
) | ||
else: | ||
logger.error( | ||
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" | ||
) | ||
except Exception as error: | ||
logger.error( | ||
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" | ||
) | ||
|
||
|
||
class Connection(BaseModel): | ||
arn = str | ||
id: str | ||
name: Optional[str] = None | ||
location: str | ||
region: str | ||
|
||
|
||
class VirtualInterface(BaseModel): | ||
arn: str | ||
id: str | ||
name: str | ||
connection_id: Optional[str] = None | ||
vgw_gateway_id: str | ||
dx_gateway_id: str | ||
location: str | ||
region: str | ||
|
||
|
||
class VirtualGateway(BaseModel): | ||
arn: str | ||
id: str | ||
vifs: list | ||
connections: list | ||
region: str | ||
|
||
|
||
class DXGateway(BaseModel): | ||
arn: str | ||
id: str | ||
vifs: list | ||
connections: list | ||
region: str |
Empty file.
34 changes: 34 additions & 0 deletions
34
...ect_virtual_interface_redundancy/directconnect_virtual_interface_redundancy.metadata.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
{ | ||
"Provider": "aws", | ||
"CheckID": "directconnect_virtual_interface_redundancy", | ||
"CheckTitle": "Ensure Direct Connect virtual interface(s) are providing redundant connections", | ||
"CheckType": [ | ||
"Resilience" | ||
], | ||
"ServiceName": "directconnect", | ||
"SubServiceName": "", | ||
"ResourceIdTemplate": "arn:partition:directconnect:region:account-id:directconnect/resource-id", | ||
"Severity": "medium", | ||
"ResourceType": "", | ||
"Description": "Checks the resilience of the AWS Direct Connect used to connect your on-premises to each Direct Connect gateway or virtual private gateway.", | ||
"Risk": "This check alerts you if any Direct Connect gateway or virtual private gateway isn't configured with virtual interfaces across at least two distinct Direct Connect locations. Lack of location resiliency can result in unexpected downtime during maintenance, a fiber cut, a device failure, or a complete location failure.", | ||
"RelatedUrl": "https://docs.aws.amazon.com/awssupport/latest/user/fault-tolerance-checks.html#amazon-direct-connect-location-resiliency", | ||
"Remediation": { | ||
"Code": { | ||
"CLI": "", | ||
"NativeIaC": "", | ||
"Other": "", | ||
"Terraform": "" | ||
}, | ||
"Recommendation": { | ||
"Text": "To build Direct Connect location resiliency, you can configure the Direct Connect gateway or virtual private gateway to connect to at least two distinct Direct Connect locations.", | ||
"Url": "https://aws.amazon.com/directconnect/resiliency-recommendation/" | ||
} | ||
}, | ||
"Categories": [ | ||
"redundancy" | ||
], | ||
"DependsOn": [], | ||
"RelatedTo": [], | ||
"Notes": "" | ||
} |
48 changes: 48 additions & 0 deletions
48
.../directconnect_virtual_interface_redundancy/directconnect_virtual_interface_redundancy.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
from prowler.lib.check.models import Check, Check_Report_AWS | ||
from prowler.providers.aws.services.directconnect.directconnect_client import ( | ||
directconnect_client, | ||
) | ||
|
||
|
||
class directconnect_virtual_interface_redundancy(Check): | ||
def execute(self): | ||
findings = [] | ||
for vgw in directconnect_client.vgws.values(): | ||
report = Check_Report_AWS(self.metadata()) | ||
report.resource_arn = vgw.arn | ||
report.region = vgw.region | ||
report.resource_id = vgw.id | ||
if len(vgw.vifs) < 2: | ||
report.status = "FAIL" | ||
report.status_extended = ( | ||
f"Virtual private gateway {vgw.id} only has one VIF." | ||
) | ||
elif len(vgw.connections) < 2: | ||
report.status = "FAIL" | ||
report.status_extended = f"Virtual private gateway {vgw.id} has more than 1 VIFs, but all the VIFs are on the same DX Connection." | ||
else: | ||
report.status = "PASS" | ||
report.status_extended = f"Virtual private gateway {vgw.id} has more than 1 VIFs and the VIFs are on more than one DX connection." | ||
|
||
findings.append(report) | ||
|
||
for dxgw in directconnect_client.dxgws.values(): | ||
report = Check_Report_AWS(self.metadata()) | ||
report.region = dxgw.region | ||
report.resource_arn = dxgw.arn | ||
report.resource_id = dxgw.id | ||
if len(dxgw.vifs) < 2: | ||
report.status = "FAIL" | ||
report.status_extended = ( | ||
f"Direct Connect gateway {dxgw.id} only has one VIF." | ||
) | ||
elif len(dxgw.connections) < 2: | ||
report.status = "FAIL" | ||
report.status_extended = f"Direct Connect gateway {dxgw.id} has more than 1 VIFs, but all the VIFs are on the same DX Connection." | ||
else: | ||
report.status = "PASS" | ||
report.status_extended = f"Direct Connect gateway {dxgw.id} has more than 1 VIFs and the VIFs are on more than one DX connection." | ||
|
||
findings.append(report) | ||
|
||
return findings |
Oops, something went wrong.