def create_as_nat_gateway(subnets, public, awscfg, ngfw, queue): vpc = VpcConfiguration(subnets[0].vpc.id).load() ngfw = NGFWConfiguration(**ngfw) logger.info('Creating NGFW as a NAT Gateway in availability zone: {} with ' 'subnets: {}'.format(subnets[0].availability_zone, subnets)) try: # Create public side network for interface 0 using the /28 network space vpc.create_network_interface( 0, cidr_block=public, availability_zone=subnets[0].availability_zone, description='stonesoft-public') # Change route tables of any subnets specified to use NAT Gateway interface = [intf.get(0) for intf in vpc.network_interface][0] for subnet in subnets: vpc.assign_route_table(subnet, interface) ngfw_init = deploy(vpc, ngfw, awscfg) task_runner(ngfw_init, queue) except (botocore.exceptions.ClientError, CreateEngineFailed, NodeCommandFailed) as e: logger.error('Caught exception, rolling back: {}'.format(e)) queue.put(('{}, {}:'.format(subnets[0].availability_zone, subnets), [str(e)])) rollback_existing_vpc(vpc, subnets) if ngfw.engine: del_fw_from_smc([ngfw.name])
def create_vpc_and_ngfw(awscfg, ngfw): ''' Use Case 1: Create entire VPC and deploy NGFW --------------------------------------------- This will fully create a VPC and associated requirements. The following will occur: * A new VPC will be created in the AZ based on boto3 client region * Two network subnets are created in the VPC, one public and one private * Two network interfaces are created and assigned to the subnets eth0 = public, eth1 = private * An elastic IP is created and attached to the public network interface * An internet gateway is created and attached to the public network interface * A route is created in the default route table for the public interface to route 0.0.0.0/0 to the IGW * The default security group is modified to allow inbound access from 0.0.0.0/0 to to the NGFW network interface :py:func:`VpcConfiguration.authorize_security_group_ingress` * A secondary route table is created with a default route to 0.0.0.0/0 with a next hop assigned to interface eth1 (NGFW). This is attached to the private subnet. * The NGFW is automatically created and UserData is obtained for AMI instance launch * AMI is launched using UserData to allow auto-connection to NGFW SMC Management * NGFW receives queued policy and becomes active ''' vpc = VpcConfiguration.create(vpc_subnet=awscfg.vpc_subnet) ngfw = NGFWConfiguration(**ngfw) try: vpc.create_network_interface(0, awscfg.vpc_public, description='public-ngfw') vpc.create_network_interface(1, awscfg.vpc_private, description='private-ngfw') vpc.security_group = create_security_group(vpc.vpc, 'stonesoft-sg') authorize_security_group_ingress(vpc.security_group, '0.0.0.0/0', ip_protocol='-1') # If user wants a client AMI, launch in the background if awscfg.aws_client_ami: logger.info('Launching client AMI with id: {}'.format( awscfg.aws_client_ami)) ngfw.aws_ami_ip = spin_up_host(awscfg.aws_keypair, vpc, awscfg.aws_client_ami) ngfw_init = deploy(vpc, ngfw, awscfg) return task_runner(ngfw_init) except (botocore.exceptions.ClientError, CreateEngineFailed, NodeCommandFailed) as e: logger.error('Caught exception, rolling back: {}'.format(e)) rollback(vpc.vpc) if ngfw.engine: del_fw_from_smc([ngfw.name]) return [('Failed deploying VPC', [str(e)])]
def rollback(vpc): """ In case of failure, convenience to wrap in try/except and remove the VPC. If there is a running EC2 instance, this will terminate that instance, remove all other dependencies and delete the VPC. This will only rollback a VPC that has the stonesoft tag. :param ec2.Vpc vpc: vpc reference """ try: if vpc.tags: has_tag = any(tag for tag in vpc.tags if tag.get('Key') == 'stonesoft') if not has_tag: raise VpcConfigurationError else: raise VpcConfigurationError except VpcConfigurationError: raise VpcConfigurationError( 'Stonesoft tag not found, cannot delete VPC.') instance_ids = [] for instance in vpc.instances.filter( Filters=[{ 'Name': 'instance-state-name', 'Values': ['running', 'pending', 'stopped'] }]): logger.info("Terminating instance: {}".format(instance.instance_id)) instance_ids.append(instance.instance_id) instance.terminate() waiter = ec2.meta.client.get_waiter('instance_terminated') waiter.wait(InstanceIds=instance_ids) # Network interfaces for intf in vpc.network_interfaces.all(): intf.delete() # Subnets for subnet in vpc.subnets.all(): subnet.delete() # Dump route tables for rt in vpc.route_tables.all(): if not rt.associations_attribute: rt.delete() else: for current in rt.associations_attribute: if not current or current.get('Main') is False: rt.delete() # Internet gateway for igw in vpc.internet_gateways.all(): igw.detach_from_vpc(VpcId=vpc.vpc_id) igw.delete() # Delete security group try: grp = list( vpc.security_groups.filter(Filters=[{ 'Name': 'group-name', 'Values': ['stonesoft-sg'] }])) if grp: grp[0].delete() except botocore.exceptions.ClientError as e: if e.response['Error']['Code'] == 'InvalidGroup.NotFound': pass else: raise vpc.delete() logger.info("Deleted vpc: {}".format(vpc.vpc_id)) del_fw_from_smc(instance_ids)
def remove_ngfw_from_vpc(instances): """ Remove only Stonesoft NGFW and related elements. Tags are used on subnets, route tables and instances to find stonesoft related components and stop in the right order. When route tables are removed, existing subnets will revert to using the Main route table. :param list ec2.Instance: call from select_instance(list_tagged_instances) """ if not instances: return vpc = instances[0].vpc logger.info('Remove instances: {}'.format(instances)) # Subnet's are last to be removed removeables = [] tagged_subnets = [tagged.id for tagged in list_tagged_subnets(vpc)] all_subnets = [untagged.id for untagged in list_all_subnets(vpc)] # Used to disassociate existing subnet route table untagged_subnets = list(set(all_subnets) - set(tagged_subnets)) for instance in instances: for dependency in instance.network_interfaces: dependency.modify_attribute( Attachment={ 'AttachmentId': dependency.attachment['AttachmentId'], 'DeleteOnTermination': True }) subnet = dependency.subnet_id if subnet in tagged_subnets: removeables.append(subnet) logger.info('Terminating instance: {}'.format(instance.instance_id)) instance.terminate() # Remove association of route tables created before deleting them. If an existing # subnet had a previous route table assigned, it will be reassigned. for rt in list_tagged_rtables(vpc): if rt.associations: for assoc in rt.associations.all(): if assoc.subnet_id in untagged_subnets: logger.info( 'Removing route tbl assoc: {} for subnet: {}'.format( assoc.route_table_id, assoc.subnet_id)) assoc.delete() # If stonesoft tag value exists with a previous route table # association, re-map it if rt.tags[0]['Value']: logger.info( 'Reassociating subnet: {} with original route ' 'table: {}'.format(assoc.subnet_id, rt.tags[0]['Value'])) try: original_rt = ec2.RouteTable(rt.tags[0]['Value']) original_rt.associate_with_subnet( SubnetId=assoc.subnet_id) except botocore.exceptions.ClientError as e: logger.error( 'Error reassigning route table. Will default back ' 'to main route table: {}'.format(e)) rt.delete() else: rt.delete() instance_ids = [ec2_instance.id for ec2_instance in instances] waiter = ec2.meta.client.get_waiter('instance_terminated') logger.info( 'Waiting for instances to fully terminate...This can take a few minutes.' ) waiter.wait(InstanceIds=instance_ids) # Remove after instance termination for subnet in removeables: ec2.Subnet(subnet).delete() del_fw_from_smc(instance_ids) logger.info('Completed successfully.')
def create_inline_ngfw(subnets, public, awscfg, ngfw, queue): ''' Use Case 2: Deploy NGFW into existing VPC ----------------------------------------- This assumes the following: * You have an existing VPC with one or more subnets * Available elastic IP per NGFW * Available /28 subnets in VPC network, one for each AZ NGFW When NGFW is injected into an existing VPC, the deployment strategy will obtain the defined network address space for the VPC. It will create a 'private' supernet using a /28 subnet on the uppermost side of the VPC network. For example, a VPC network of 172.16.0.0/16 will result in a new subnet created 172.31.255.240/28. This will act as the 'public' side of the NGFW (creating a small subnet prevents wasted address space). This public side network will use the "Main" route table that should already use the AWS Internet Gateway as it's next hop, allowing internet connectivity. Next, each a network interface will be created in each subnet that exists in the VPC. AWS will assign an address during interface creation which will be assigned to the NGFW on that subnet segment. In addition, a new route table will be created for each subnet and direct 0.0.0.0/0 to the NGFW network interface previously created. This results in each subnet having the NGFW as it's gateway for outbound traffic. .. note:: Each subnet must be in the same AZ, which is why it is necessary to create a unique /28 for each subnet if spread across AZ's. http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-eni.html :param list ec2.Subnet subnets: subnets to inject ngfw :param str public: public network for ngfw instance :param AWSConfig awscfg: aws config :param dict ngfw: ngfw configuration :param Queue queue: reference to result queue ''' vpc = VpcConfiguration(subnets[0].vpc.id).load() ngfw = NGFWConfiguration(**ngfw) logger.info( 'Creating NGFW as Inline Gateway in availability zone: {} with ' 'subnets: {}'.format(subnets[0].availability_zone, subnets)) try: # Create public side network for interface 0 using the /28 network space vpc.create_network_interface( 0, cidr_block=public, availability_zone=subnets[0].availability_zone, description='stonesoft-public') # Each ec2 Subnet gets it's own network interface and route table intf = 1 for subnet in subnets: vpc.create_network_interface(intf, ec2_subnet=subnet, description='stonesoft-private') intf += 1 ngfw_init = deploy(vpc, ngfw, awscfg) task_runner(ngfw_init, queue) except (botocore.exceptions.ClientError, CreateEngineFailed, NodeCommandFailed) as e: logger.error('Caught exception, rolling back: {}'.format(e)) queue.put(('{}, {}:'.format(subnets[0].availability_zone, subnets), [str(e)])) rollback_existing_vpc(vpc, subnets) if ngfw.engine: del_fw_from_smc([ngfw.name])