Exemplo n.º 1
0
    def init(self):
        # Network Stack Templates
        # VPC Stack
        vpc_config = self.env_ctx.env_region.network.vpc
        if vpc_config == None:
            # NetworkEnvironment with no network - serverless
            return
        network_config = get_parent_by_interface(vpc_config, schemas.INetwork)
        vpc_config.resolve_ref_obj = self
        vpc_config.private_hosted_zone.resolve_ref_obj = self
        self.vpc_stack = self.add_new_stack(
            self.region,
            vpc_config,
            paco.cftemplates.VPC,
            stack_tags=StackTags(self.stack_tags),
        )

        # Segments
        self.segment_list = []
        self.segment_dict = {}
        segments = network_config.vpc.segments
        for segment in segments.values():
            segment.resolve_ref_obj = self
            segment_stack = self.add_new_stack(
                self.region,
                segment,
                paco.cftemplates.Segment,
                stack_tags=StackTags(self.stack_tags),
                stack_orders=[StackOrder.PROVISION],
                extra_context={'env_ctx': self.env_ctx},
            )
            self.segment_dict[segment.name] = segment_stack
            self.segment_list.append(segment_stack)

        # Security Groups
        sg_config = network_config.vpc.security_groups
        self.sg_list = []
        self.sg_dict = {}
        # EC2 NATGateway Security Groups
        # Creates a security group for each Availability Zone in the segment
        sg_nat_id = 'bastion_nat_' + utils.md5sum(str_data='gateway')[:8]
        for nat_config in vpc_config.nat_gateway.values():
            if nat_config.is_enabled() == False:
                continue
            if nat_config.type == 'EC2':
                sg_nat_config_dict = {}
                if sg_nat_id not in sg_config.keys():
                    sg_config[sg_nat_id] = paco.models.networks.SecurityGroups(
                        sg_nat_id, sg_config)
                for az_idx in range(1, network_config.availability_zones + 1):
                    sg_nat_config_dict['enabled'] = True
                    sg_nat_config_dict['ingress'] = []
                    for route_segment in nat_config.default_route_segments:
                        route_segment_id = route_segment.split('.')[-1]
                        az_cidr = getattr(
                            vpc_config.segments[route_segment_id],
                            f"az{az_idx}_cidr")
                        sg_nat_config_dict['ingress'].append({
                            'name': 'SubnetAZ',
                            'cidr_ip': az_cidr,
                            'protocol': '-1'
                        })
                    sg_nat_config_dict['egress'] = [{
                        'name': 'ANY',
                        'cidr_ip': '0.0.0.0/0',
                        'protocol': '-1'
                    }]
                    sg_nat_rule_id = nat_config.name + '_az' + str(az_idx)
                    sg_config[sg_nat_id][
                        sg_nat_rule_id] = paco.models.networks.SecurityGroup(
                            sg_nat_rule_id, vpc_config)
                    paco.models.loader.apply_attributes_from_config(
                        sg_config[sg_nat_id][sg_nat_rule_id],
                        sg_nat_config_dict)

        # Declared Security Groups
        for sg_id in sg_config:
            # Set resolve_ref_obj
            for sg_obj_id in sg_config[sg_id]:
                sg_config[sg_id][sg_obj_id].resolve_ref_obj = self
            sg_stack = self.add_new_stack(
                self.region,
                sg_config[sg_id],
                paco.cftemplates.SecurityGroups,
                stack_tags=StackTags(self.stack_tags),
                extra_context={
                    'env_ctx': self.env_ctx,
                    'template_type': 'Groups'
                },
            )
            self.sg_list.append(sg_stack)
            self.sg_dict[sg_id] = sg_stack

        # Ingress/Egress Stacks
        for sg_id in sg_config:
            self.add_new_stack(self.region,
                               sg_config[sg_id],
                               paco.cftemplates.SecurityGroups,
                               stack_tags=StackTags(self.stack_tags),
                               extra_context={
                                   'env_ctx': self.env_ctx,
                                   'template_type': 'Rules'
                               })

        # Wait for Segment Stacks
        for segment_stack in self.segment_list:
            self.add_stack_order(segment_stack, [StackOrder.WAIT])

        # VPC Peering Stack
        if vpc_config.peering != None:
            peering_config = self.env_ctx.env_region.network.vpc.peering
            for peer_id in peering_config.keys():
                peer_config = vpc_config.peering[peer_id]
                peer_config.resolve_ref_obj = self
                # Add role to the target network account
                if peer_config.network_environment != None and peer_config.peer_type == 'accepter':
                    netenv_ref = Reference(peer_config.network_environment +
                                           '.network')
                    requester_netenv_config = netenv_ref.resolve(
                        self.paco_ctx.project)
                    requester_account_id = self.paco_ctx.get_ref(
                        requester_netenv_config.aws_account + '.id')
                    accepter_vpc_id = self.paco_ctx.get_ref(
                        vpc_config.paco_ref + '.id')
                    # Only create the role if we are cross account
                    if self.account_ctx.id != requester_account_id:
                        self.gen_vpc_peering_accepter_role(
                            peer_config, vpc_config, accepter_vpc_id,
                            requester_account_id)
            self.peering_stack = self.add_new_stack(
                self.region,
                vpc_config.peering,
                paco.cftemplates.VPCPeering,
                stack_tags=StackTags(self.stack_tags),
            )

        # NAT Gateway
        self.nat_list = []
        for nat_config in vpc_config.nat_gateway.values():
            if sg_nat_id in sg_config.keys():
                nat_sg_config = sg_config[sg_nat_id]
            else:
                nat_sg_config = None
            # We now disable the NAT Gateway in the template so that we can delete it and recreate it when disabled.
            nat_stack = self.add_new_stack(
                self.region,
                nat_config,
                paco.cftemplates.NATGateway,
                stack_tags=StackTags(self.stack_tags),
                stack_orders=[StackOrder.PROVISION],
                extra_context={'nat_sg_config': nat_sg_config},
            )
            self.nat_list.append(nat_stack)

        for nat_stack in self.nat_list:
            self.add_stack_order(nat_stack, [StackOrder.WAIT])

        # VPC Endpoints
        vpc_endpoints_stack = self.add_new_stack(
            self.region,
            vpc_config,
            paco.cftemplates.VPCEndpoints,
            stack_tags=StackTags(self.stack_tags),
            stack_orders=[StackOrder.PROVISION])
        self.add_stack_order(vpc_endpoints_stack, [StackOrder.WAIT])
Exemplo n.º 2
0
    def ec2_nat_gateway(self, network_config, nat_sg_config, nat_sg_config_ref,
                        nat_config):

        nat_az = nat_config.availability_zone
        nat_segment = nat_config.segment.split('.')[-1]
        ec2_resource = {}
        for az_idx in range(1, network_config.availability_zones + 1):
            # Add security groups created for NAT Bastions
            nat_security_groups = []
            nat_security_groups.extend(nat_config.security_groups)
            if nat_az == 'all':
                nat_sg_id = nat_config.name + "_az" + str(az_idx)
                nat_security_groups.append('paco.ref ' + nat_sg_config_ref +
                                           '.' + nat_sg_id)
            elif az_idx == int(nat_config.availability_zone):
                for nat_sg_id in nat_sg_config.keys():
                    nat_security_groups.append('paco.ref ' +
                                               nat_sg_config_ref + '.' +
                                               nat_sg_id)

            if nat_az == 'all' or nat_az == str(az_idx):
                security_group_list_param = self.create_cfn_ref_list_param(
                    param_type='List<AWS::EC2::SecurityGroup::Id>',
                    name='NATSecurityGroupListAZ' + str(az_idx),
                    description=
                    'List of security group ids to attach to the instances.',
                    value=nat_security_groups,
                    ref_attribute='id',
                )

                subnet_id_param = self.create_cfn_parameter(
                    name=self.create_cfn_logical_id_join(
                        str_list=['SubnetIdAZ',
                                  str(az_idx), nat_segment],
                        camel_case=True),
                    param_type='String',
                    description='SubnetId to launch an EC2 NAT instance',
                    value=nat_config.segment + '.az' + str(az_idx) +
                    '.subnet_id',
                )
                ref_parts = nat_config.paco_ref_parts.split('.')
                instance_name = utils.big_join(str_list=[
                    ref_parts[1], ref_parts[2], 'NGW', nat_config.name,
                    'AZ' + str(az_idx)
                ],
                                               separator_ch='-',
                                               camel_case=True)
                # ToDo: expose latest ami id as an API and call it directly
                # SLOW: takes a couple seconds to resolve this every Paco run
                latest_image_ref = Reference(
                    'paco.ref function.aws.ec2.ami.latest.amazon-linux-nat')
                latest_image_ref.set_region(self.aws_region)
                nat_ami_id = latest_image_ref.resolve(self.paco_ctx.project,
                                                      self.account_ctx)
                ec2_resource[az_idx] = troposphere.ec2.Instance(
                    title=self.create_cfn_logical_id_join(
                        str_list=['EC2NATInstance',
                                  str(az_idx)],
                        camel_case=True),
                    template=self.template,
                    SubnetId=troposphere.Ref(subnet_id_param),
                    ImageId=nat_ami_id,
                    InstanceType=nat_config.ec2_instance_type,
                    KeyName=self.paco_ctx.get_ref(nat_config.ec2_key_pair +
                                                  '.keypair_name'),
                    SecurityGroupIds=troposphere.Ref(
                        security_group_list_param),
                    SourceDestCheck=False,
                    Tags=troposphere.ec2.Tags(Name=instance_name))

                ec2_instance_id_output = troposphere.Output(
                    title=ec2_resource[az_idx].title + 'Id',
                    Description="EC2 NAT Instance Id",
                    Value=troposphere.Ref(ec2_resource[az_idx]))
                self.template.add_output(ec2_instance_id_output)

                troposphere.ec2.EIP(title=self.create_cfn_logical_id_join(
                    str_list=['ElasticIP', str(az_idx)], camel_case=True),
                                    template=self.template,
                                    Domain='vpc',
                                    InstanceId=troposphere.Ref(
                                        ec2_resource[az_idx]))

                self.register_stack_output_config(
                    nat_config.paco_ref_parts + ".ec2.az" + str(az_idx),
                    ec2_instance_id_output.title)

        # Add DefaultRoute to the route tables in each AZ
        for segment_ref in nat_config.default_route_segments:
            segment_id = segment_ref.split('.')[-1]
            # Routes
            for az_idx in range(1, network_config.availability_zones + 1):
                if nat_config.availability_zone == 'all':
                    instance_id_ref = troposphere.Ref(ec2_resource[az_idx])
                else:
                    instance_id_ref = troposphere.Ref(
                        ec2_resource[int(nat_az)])

                route_table_id_param = self.create_cfn_parameter(
                    name=self.create_cfn_logical_id_join(
                        str_list=['RouteTable', segment_id, 'AZ',
                                  str(az_idx)],
                        camel_case=True),
                    param_type='String',
                    description='RouteTable ID for ' + segment_id + ' AZ' +
                    str(az_idx),
                    value=segment_ref + ".az{}.route_table.id".format(az_idx),
                )

                troposphere.ec2.Route(
                    title="EC2NATRouteAZ" + str(az_idx),
                    template=self.template,
                    DestinationCidrBlock="0.0.0.0/0",
                    InstanceId=instance_id_ref,
                    RouteTableId=troposphere.Ref(route_table_id_param))