Skip to content
This repository has been archived by the owner on May 13, 2021. It is now read-only.

Add support for reconciliation of router configs #18

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 47 additions & 2 deletions deployment/transit-vpc-primary-account-existing-vpc.template
Original file line number Diff line number Diff line change
Expand Up @@ -549,8 +549,21 @@
{
"Effect": "Allow",
"Action": [
"s3:GetBucketLocation",
"s3:ListBucket",
"s3:HeadBucket"
],
"Resource": { "Fn::Join": ["", ["arn:aws:s3:::", { "Ref" : "VPNConfigS3Bucket" } ]] }
},
{
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:GetObject"
"s3:GetObject",
"s3:CopyObject",
"s3:DeleteObject",
"s3:ListObjects",
"s3:ListObjectsV2"
],
"Resource": { "Fn::Join": ["", ["arn:aws:s3:::", { "Ref" : "VPNConfigS3Bucket" }, "/", {"Ref": "S3Prefix"}, "*" ]] }
}
Expand Down Expand Up @@ -581,6 +594,8 @@
},
"Environment": {
"Variables": {
"BUCKET_NAME": { "Ref" : "VPNConfigS3Bucket" },
"BUCKET_PREFIX": { "Ref" : "S3Prefix" },
"CONFIG_FILE": "transit_vpc_config.txt",
"LOG_LEVEL":"INFO"
}
Expand Down Expand Up @@ -613,6 +628,27 @@
]] }
}
},
"CiscoConfigScheduleEvent": {
"Type": "AWS::Events::Rule",
"Properties": {
"Description": "Transit VPC: Rule to trigger Cisco Configurator to reconcile CSR against VPN config files in S3.",
"ScheduleExpression": "cron(0 0/6 * * ? *)",
"State": "ENABLED",
"Targets": [ {
"Id": { "Fn::Join": ["-", [ { "Ref" : "AWS::StackName" },"CiscoConfig-6hour" ]] },
"Arn": { "Fn::GetAtt": [ "CiscoConfigFunction", "Arn" ] }
} ]
}
},
"PermissionForCiscoConfigScheduleEvent": {
"Type": "AWS::Lambda::Permission",
"Properties": {
"FunctionName": { "Ref": "CiscoConfigFunction" },
"Action": "lambda:InvokeFunction",
"Principal": "events.amazonaws.com",
"SourceArn": { "Fn::GetAtt": ["CiscoConfigScheduleEvent", "Arn"] }
}
},
"TransitVpcS3Config": {
"Type": "Custom::LoadLambda",
"Properties": {
Expand Down Expand Up @@ -690,9 +726,18 @@
{
"Effect": "Allow",
"Action": [
"s3:ListBucket"
],
"Resource": { "Fn::Join": ["", ["arn:aws:s3:::", { "Ref" : "VPNConfigS3Bucket" } ]] }
},
{
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:PutObjectAcl",
"s3:GetObject"
"s3:GetObject",
"s3:ListObjects",
"s3:ListObjectsV2"
],
"Resource": { "Fn::Join": ["", ["arn:aws:s3:::", { "Ref" : "VPNConfigS3Bucket" }, "/", {"Ref": "S3Prefix"}, "*" ]] }
}
Expand Down
49 changes: 47 additions & 2 deletions deployment/transit-vpc-primary-account.template
Original file line number Diff line number Diff line change
Expand Up @@ -635,8 +635,21 @@
{
"Effect": "Allow",
"Action": [
"s3:GetBucketLocation",
"s3:ListBucket",
"s3:HeadBucket"
],
"Resource": { "Fn::Join": ["", ["arn:aws:s3:::", { "Ref" : "VPNConfigS3Bucket" } ]] }
},
{
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:GetObject"
"s3:GetObject",
"s3:CopyObject",
"s3:DeleteObject",
"s3:ListObjects",
"s3:ListObjectsV2"
],
"Resource": { "Fn::Join": ["", ["arn:aws:s3:::", { "Ref" : "VPNConfigS3Bucket" }, "/", {"Ref": "S3Prefix"}, "*" ]] }
}
Expand Down Expand Up @@ -667,6 +680,8 @@
},
"Environment": {
"Variables": {
"BUCKET_NAME": { "Ref" : "VPNConfigS3Bucket" },
"BUCKET_PREFIX": { "Ref" : "S3Prefix" },
"CONFIG_FILE": "transit_vpc_config.txt",
"LOG_LEVEL":"INFO"
}
Expand Down Expand Up @@ -699,6 +714,27 @@
]] }
}
},
"CiscoConfigScheduleEvent": {
"Type": "AWS::Events::Rule",
"Properties": {
"Description": "Transit VPC: Rule to trigger Cisco Configurator to reconcile CSR against VPN config files in S3.",
"ScheduleExpression": "cron(0 0/6 * * ? *)",
"State": "ENABLED",
"Targets": [ {
"Id": { "Fn::Join": ["-", [ { "Ref" : "AWS::StackName" },"CiscoConfig-6hour" ]] },
"Arn": { "Fn::GetAtt": [ "CiscoConfigFunction", "Arn" ] }
} ]
}
},
"PermissionForCiscoConfigScheduleEvent": {
"Type": "AWS::Lambda::Permission",
"Properties": {
"FunctionName": { "Ref": "CiscoConfigFunction" },
"Action": "lambda:InvokeFunction",
"Principal": "events.amazonaws.com",
"SourceArn": { "Fn::GetAtt": ["CiscoConfigScheduleEvent", "Arn"] }
}
},
"TransitVpcS3Config": {
"Type": "Custom::LoadLambda",
"Properties": {
Expand Down Expand Up @@ -776,9 +812,18 @@
{
"Effect": "Allow",
"Action": [
"s3:ListBucket"
],
"Resource": { "Fn::Join": ["", ["arn:aws:s3:::", { "Ref" : "VPNConfigS3Bucket" } ]] }
},
{
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:PutObjectAcl",
"s3:GetObject"
"s3:GetObject",
"s3:ListObjects",
"s3:ListObjectsV2"
],
"Resource": { "Fn::Join": ["", ["arn:aws:s3:::", { "Ref" : "VPNConfigS3Bucket" }, "/", {"Ref": "S3Prefix"}, "*" ]] }
}
Expand Down
50 changes: 50 additions & 0 deletions source/transit-vpc-poller/transit-vpc-poller.py
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,7 @@ def lambda_handler(event, context):
log.info("Cleaned up %s since it has no VPN connections left", vpn['CustomerGatewayId'])
except:
log.debug("%s still has existing VPN connections", vpn['CustomerGatewayId'])
processed_vgw = True
sendAnonymousData(config, vgwTags, region_id, 1)

# if a VGW has been processed, then we need to break out of VGW processing
Expand All @@ -259,3 +260,52 @@ def lambda_handler(event, context):
# if a VGW has been processed, then we need to break out of region processing
if processed_vgw:
break

#If no VGW has been processed in any region this cycle, then check for orphaned VPN configs
if not processed_vgw:
ec2=boto3.client('ec2',region_name=os.environ['AWS_DEFAULT_REGION'])
vpns=ec2.describe_vpn_connections(Filters=[
{'Name':'state','Values':['available','pending','deleting']},
{'Name':'tag:'+config['HUB_TAG'],'Values':[config['HUB_TAG_VALUE']]}
])
#Make sure we don't proceed if describe returned no connections but didn't throw an error
if 'VpnConnections' in vpns and vpns['VpnConnections']:
for csrNum in ['1','2']:
#Make sure we only check for orphans from the account running the designated CSR
csr_addresses = ec2.describe_addresses(
Filters = [{'Name':'public-ip', 'Values':[config['EIP'+csrNum]]}]
)
if not 'Addresses' in csr_addresses or not csr_addresses['Addresses']:
continue
csr_instance_owner = csr_addresses['Addresses'][0]['NetworkInterfaceOwnerId']
lambda_owner = context.invoked_function_arn.split(":")[4]
if csr_instance_owner == lambda_owner:
log.info('CSR%s is local to this lambda, checking for orphaned VPN configs', csrNum)
vpn_config_prefix=bucket_prefix+'CSR'+csrNum+'/'
vpn_configs = s3.list_objects_v2(
Bucket=bucket_name,
Prefix=vpn_config_prefix
)
#Get the list of configs from S3, and match against known VPN connections
if 'Contents' in vpn_configs:
for vpn_config in vpn_configs['Contents']:
m = re.search(r'(?P<ID>vpn-.+)\.conf$', vpn_config['Key'])
if m:
vpn_connection_id=m.group('ID')
if any (x['VpnConnectionId'] == vpn_connection_id for x in vpns['VpnConnections']):
log.debug('VPN Connection found for %s', vpn_config['Key'])
else:
log.warning('Setting orphan VPN config %s to delete', vpn_config['Key'])
#Read in the config and change status
config_orphan=s3.get_object(Bucket=bucket_name,Key=vpn_config['Key'])
xmldoc=minidom.parseString(config_orphan['Body'].read())
xmldoc.getElementsByTagName("status")[0].firstChild.data = 'delete'
#Save the new config
s3.put_object(
Body=str.encode(str(xmldoc.toxml())),
Bucket=bucket_name,
Key=vpn_config['Key'],
ACL='bucket-owner-full-control',
ServerSideEncryption='aws:kms',
SSEKMSKeyId=config['KMS_KEY']
)
Loading