Exemple #1
0
def amazon_security_group(name, vpc):
    """Create a security group authorizing access to aws services.

    :param vpc: vpc in which to create the group
    :type vpc: VPC
    :return: a security group
    :rtype: SecurityGroup
    """
    ip_ranges = requests.get(IP_RANGES_URL).json()['prefixes']

    # Retrieve first the complete list of ipv4 ip ranges for a given region
    amazon_ip_ranges = {
        k['ip_prefix']
        for k in ip_ranges if k['region'] == vpc.region and 'ip_prefix' in k
        and k['service'] == 'AMAZON'
    }

    # Sustract the list of ip ranges corresponding to EC2 instances
    ec2_ip_ranges = {
        k['ip_prefix']
        for k in ip_ranges if k['region'] == vpc.region and 'ip_prefix' in k
        and k['service'] == 'EC2'
    }
    amazon_ip_ranges -= ec2_ip_ranges

    # Authorize https on the resulting list of ip ranges
    # Note: the limit of rules per security group is set to 50 at AWS.
    # In case the number of ip ranges returned by Amazon would be greater
    # than that there would be need to split into several security groups
    sg = SecurityGroup(name, vpc, description='Allow acces to amazon services')
    for ip_range in amazon_ip_ranges:
        sg.add_rule(Ipv4EgressRule('https', ip_range))

    return sg
Exemple #2
0
def github_security_groups(name, vpc, protocol):
    """Create a dict of security group authorizing access to github services.

    As the number of rules per security group is limited to 50,
    we create blocks of 50 rules.

    :param vpc: vpc in which to create the group
    :type vpc: VPC
    :param protocol: protocol to allow (https, ssh)
    :type protocol: str
    :return: a dict of security groups indexed by name
    :rtype: dict(str, SecurityGroup)
    """
    ip_ranges = requests.get("https://api.github.com/meta").json()["git"]

    # Authorize ssh on the resulting list of ip ranges
    sgs = {}
    i = 0
    limit = 50
    sg_name = name + str(i)
    sg = SecurityGroup(sg_name, vpc, description="Allow access to github")
    sgs[sg_name] = sg
    for ip_range in ip_ranges:
        if len(sg.egress + sg.ingress) == limit:
            i += 1
            sg_name = name + str(i)
            sg = SecurityGroup(
                sg_name, vpc, description=f"Allow access to GitHub {protocol}")
            sgs[sg_name] = sg
        sg.add_rule(Ipv4EgressRule(protocol, ip_range))
    return sgs
Exemple #3
0
def amazon_security_groups(name, vpc):
    """Create a dict of security group authorizing access to aws services.

    As the number of rules per security group is limited to 60,
    we create blocks of 60 rules.

    :param vpc: vpc in which to create the group
    :type vpc: VPC
    :return: a dict of security groups indexed by name
    :rtype: dict(str, SecurityGroup)
    """
    def select_region(ip_range_record):
        """Select the VPN region and the us-east-1 region.

        Note that some global interface (e.g. sts) are only available in
        the us-east-1 region.
        """
        return ip_range_record["region"] in (vpc.region, "us-east-1")

    ip_ranges = requests.get(IP_RANGES_URL).json()["prefixes"]

    # Retrieve first the complete list of ipv4 ip ranges for a given region
    amazon_ip_ranges = {
        k["ip_prefix"]
        for k in ip_ranges
        if select_region(k) and "ip_prefix" in k and k["service"] == "AMAZON"
    }

    # Substract the list of ip ranges corresponding to services
    # that we do no need to access to or that we access through VPC
    # endpoints to limit the number of security groups and rules.
    services_used = ("AMAZON", "S3")
    removable_ip_ranges = {
        k["ip_prefix"]
        for k in ip_ranges if select_region(k) and "ip_prefix" in k
        and k["service"] not in services_used
    }
    amazon_ip_ranges -= removable_ip_ranges

    # Authorize https on the resulting list of ip ranges
    sgs = {}
    i = 0
    limit = 60
    sg_name = name + str(i)
    sg = SecurityGroup(sg_name,
                       vpc,
                       description="Allow access to amazon services")
    sgs[sg_name] = sg
    for ip_range in amazon_ip_ranges:
        if len(sg.egress + sg.ingress) == limit:
            i += 1
            sg_name = name + str(i)
            sg = SecurityGroup(sg_name,
                               vpc,
                               description="Allow acces to amazon services")
            sgs[sg_name] = sg
        sg.add_rule(Ipv4EgressRule("https", ip_range))
    return sgs
Exemple #4
0
    def add_network_access(self, protocol, cidr_block='0.0.0.0/0'):
        """Authorize some ooutbound protocols for internal servers.

        :param protocol: protocol name
        :type protocol: str
        :param cidr_block: allowed IP range (default is all)
        :type cird_block: str
        """
        self[self.name + 'InternalSG'].add_rule(
            Ipv4EgressRule(protocol, cidr_block))
Exemple #5
0
def amazon_security_groups(name, vpc):
    """Create a dict of security group authorizing access to aws services.

    As the number of rules per security group is limited to 50,
    we create blocks of 50 rules.

    :param vpc: vpc in which to create the group
    :type vpc: VPC
    :return: a dict of security groups indexed by name
    :rtype: dict(str, SecurityGroup)
    """
    ip_ranges = requests.get(IP_RANGES_URL).json()['prefixes']

    # Retrieve first the complete list of ipv4 ip ranges for a given region
    amazon_ip_ranges = {
        k['ip_prefix']
        for k in ip_ranges if k['region'] == vpc.region and 'ip_prefix' in k
        and k['service'] == 'AMAZON'
    }

    # Sustract the list of ip ranges corresponding to EC2 instances
    ec2_ip_ranges = {
        k['ip_prefix']
        for k in ip_ranges if k['region'] == vpc.region and 'ip_prefix' in k
        and k['service'] == 'EC2'
    }
    amazon_ip_ranges -= ec2_ip_ranges

    # Authorize https on the resulting list of ip ranges
    sgs = {}
    i = 0
    limit = 50
    sg_name = name + str(i)
    sg = SecurityGroup(sg_name,
                       vpc,
                       description='Allow acces to amazon services')
    sgs[sg_name] = sg
    for ip_range in amazon_ip_ranges:
        if len(sg.egress + sg.ingress) == limit:
            i += 1
            sg_name = name + str(i)
            sg = SecurityGroup(sg_name,
                               vpc,
                               description='Allow acces to amazon services')
            sgs[sg_name] = sg
        sg.add_rule(Ipv4EgressRule('https', ip_range))
    return sgs
Exemple #6
0
    def add_network_access(
        self,
        protocol: str,
        cidr_block: str = "0.0.0.0/0",
        from_port: Optional[int] = None,
        to_port: Optional[int] = None,
    ) -> None:
        """Authorize some outbound protocols for internal servers.

        :param protocol: protocol name
        :param cidr_block: allowed IP range (default is all)
        :param from_port: optional starting port
        :param to_port: optional ending port
        """
        self[self.name + "InternalSG"].add_rule(
            Ipv4EgressRule(protocol,
                           cidr_block,
                           from_port=from_port,
                           to_port=to_port))
Exemple #7
0
    def __init__(
        self,
        name,
        internal_server_policy,
        bastion_ami=None,
        allow_ssh_from=None,
        description=None,
        vpc_cidr_block="10.10.0.0/16",
        private_cidr_block="10.10.0.0/17",
        public_cidr_block="10.10.128.0/18",
        aws_endpoints_cidr_block="10.10.192.0/18",
    ):
        """Create a VPC Fortress.

        This create a vpc with a public and a private subnet. Servers in the
        private subnet are only accessible through a bastion machine declared
        in the public subnet. An additional subnet is created to host AWS
        services endpoints network interfaces.

        :param name: stack name
        :type name: str
        :param internal_server_policy: policy associated with instance role
            of private servers
        :type internal_server_policy: Policy
        :param bastion_ami: AMI used for the bastion server. If None no bastion
            is setup
        :type bastion_ami: AMI | None
        :param allow_ssh_from: ip ranges from which ssh can be done to the
            bastion. if bastion_ami is None, parameter is discarded
        :type allow_ssh_from: str | None
        :param vpc_cidr_block: ip ranges for the associated vpc
        :type vpc_cidr_block: str
        :param private_cidr_block: ip ranges (subset of vpc_cidr_block) used
            for private subnet
        :type private_cidr_block: str
        :param public_cidr_block: ip ranges (subset of vpc_cidr_block) used
            for public subnet
        :type public_cidr_block: str
        :param aws_endpoints_cidr_block: ip ranges (subset of vpc_cidr_block) used
            for aws endpoints
        :type aws_endpoints_cidr_block: str
        """
        super().__init__(name, description)

        # Create VPC along with the three subnets
        self.add(VPCStack(self.name + "VPC", vpc_cidr_block))
        self.vpc.add_subnet(self.name + "PublicNet",
                            public_cidr_block,
                            is_public=True,
                            use_nat=True)
        self.vpc.add_subnet(self.name + "PrivateNet",
                            private_cidr_block,
                            nat_to=self.name + "PublicNet")
        self.vpc.add_subnet(self.name + "AWSEndpointsNet",
                            aws_endpoints_cidr_block)

        self.amazon_groups = {}
        self.github_groups = {}

        if bastion_ami is not None:
            # Allow ssh to bastion only from a range of IP address
            self.add(
                SecurityGroup(
                    self.name + "BastionSG",
                    self.vpc.vpc,
                    description="security group for bastion servers",
                    rules=[
                        Ipv4IngressRule("ssh", cidr) for cidr in allow_ssh_from
                    ],
                ))

            # Create the bastion
            self.add(Instance(self.name + "Bastion", bastion_ami))
            self.bastion.tags["Name"] = "Bastion (%s)" % self.name
            self.bastion.add(
                EC2NetworkInterface(
                    self.public_subnet.subnet,
                    public_ip=True,
                    groups=[self[self.name + "BastionSG"]],
                ))

            # Create security group for internal servers
            self.add(
                SecurityGroup(
                    self.name + "InternalSG",
                    self.vpc.vpc,
                    description=("Allow ssh inside VPC and allow https "
                                 "to VPC endpoints subnet"),
                    rules=[
                        Ipv4IngressRule("ssh", self.public_subnet.cidr_block),
                        Ipv4EgressRule("https",
                                       self.aws_endpoints_subnet.cidr_block),
                    ],
                ))
        else:
            # If no bastion is used do not authorize ssh inside the vpc
            self.add(
                SecurityGroup(
                    self.name + "InternalSG",
                    self.vpc.vpc,
                    description=("Do not allow ssh inside VPC but allow https "
                                 "to the VPC endpoints subnet."),
                    rules=[
                        Ipv4EgressRule("https",
                                       self.aws_endpoints_subnet.cidr_block)
                    ],
                ))

        # Create security group for endpoints
        self.add(
            SecurityGroup(
                self.name + "InterfaceEndpointsSG",
                self.vpc.vpc,
                description=("Allow https from the private subnet"),
                rules=[
                    Ipv4IngressRule("https", self.private_subnet.cidr_block)
                ],
            ))

        ir = InstanceRole(self.name + "PrivServerInstanceRole")
        ir.add_policy(internal_server_policy)
        self.add(ir)