示例#1
0
文件: firewall.py 项目: Yelp/paasta
def _inbound_traffic_rule(conf, service_name, instance_name, protocol="tcp"):
    """Return iptables rules for inbound traffic

    If this is set to "reject", this is limited only to traffic from localhost"""
    policy = conf.get_inbound_firewall()
    if policy == "reject":
        for port in _nerve_ports_for_service_instance(service_name,
                                                      instance_name):
            yield iptables.Rule(
                protocol=protocol,
                src="0.0.0.0/0.0.0.0",
                dst="0.0.0.0/0.0.0.0",
                target="REJECT",
                matches=((protocol, (("dport", (str(port), )), )), ),
                target_parameters=((("reject-with",
                                     ("icmp-port-unreachable", ))), ),
            )
            for ip_range in INBOUND_PRIVATE_IP_RANGES:
                yield iptables.Rule(
                    protocol=protocol,
                    src=ip_range,
                    dst="0.0.0.0/0.0.0.0",
                    target="ACCEPT",
                    matches=((protocol, (("dport", (str(port), )), )), ),
                    target_parameters=(),
                )
示例#2
0
def _ensure_internet_chain():
    iptables.ensure_chain(
        "PAASTA-INTERNET",
        (
            iptables.Rule(
                protocol="ip",
                src="0.0.0.0/0.0.0.0",
                dst="0.0.0.0/0.0.0.0",
                target="ACCEPT",
                matches=(),
                target_parameters=(),
            ),
        )
        + tuple(
            iptables.Rule(
                protocol="ip",
                src="0.0.0.0/0.0.0.0",
                dst=ip_range,
                target="RETURN",
                matches=(),
                target_parameters=(),
            )
            for ip_range in OUTBOUND_PRIVATE_IP_RANGES
        ),
    )
示例#3
0
def _ensure_common_chain():
    """The common chain allows access for all services to certain resources."""
    iptables.ensure_chain(
        'PAASTA-COMMON',
        (
            # Allow return traffic for incoming connections
            iptables.Rule(
                protocol='ip',
                src='0.0.0.0/0.0.0.0',
                dst='0.0.0.0/0.0.0.0',
                target='ACCEPT',
                matches=(('conntrack', (('ctstate', ('ESTABLISHED', )), )), ),
                target_parameters=(),
            ),
            _yocalhost_rule(1463, 'scribed'),
            _yocalhost_rule(8125, 'metrics-relay', protocol='udp'),
            _yocalhost_rule(3030, 'sensu'),
            iptables.Rule(
                protocol='ip',
                src='0.0.0.0/0.0.0.0',
                dst='0.0.0.0/0.0.0.0',
                target='PAASTA-DNS',
                matches=(),
                target_parameters=(),
            ),
        ),
    )
def _ensure_common_chain():
    """The common chain allows access for all services to certain resources."""
    iptables.ensure_chain(
        "PAASTA-COMMON",
        (
            # Allow return traffic for incoming connections
            iptables.Rule(
                protocol="ip",
                src="0.0.0.0/0.0.0.0",
                dst="0.0.0.0/0.0.0.0",
                target="ACCEPT",
                matches=(("conntrack", (("ctstate", ("ESTABLISHED", )), )), ),
                target_parameters=(),
            ),
            _yocalhost_rule(1463, "scribed"),
            _yocalhost_rule(8125, "metrics-relay", protocol="udp"),
            _yocalhost_rule(3030, "sensu"),
            iptables.Rule(
                protocol="ip",
                src="0.0.0.0/0.0.0.0",
                dst="0.0.0.0/0.0.0.0",
                target="PAASTA-DNS",
                matches=(),
                target_parameters=(),
            ),
        ),
    )
示例#5
0
def _well_known_rules(conf):
    # Allow access to certain resources for all services by default.
    yield iptables.Rule(
        protocol='ip',
        src='0.0.0.0/0.0.0.0',
        dst='0.0.0.0/0.0.0.0',
        target='PAASTA-COMMON',
        matches=(),
        target_parameters=(),
    )

    for dep in conf.get_dependencies():
        resource = dep.get('well-known')
        if resource == 'internet':
            yield iptables.Rule(
                protocol='ip',
                src='0.0.0.0/0.0.0.0',
                dst='0.0.0.0/0.0.0.0',
                target='PAASTA-INTERNET',
                matches=(),
                target_parameters=(),
            )
        elif resource is not None:
            # TODO: handle better
            raise AssertionError(resource)
示例#6
0
def _ensure_dns_chain():
    iptables.ensure_chain(
        "PAASTA-DNS",
        tuple(
            itertools.chain.from_iterable(
                (
                    iptables.Rule(
                        protocol="udp",
                        src="0.0.0.0/0.0.0.0",
                        dst=f"{dns_server}/255.255.255.255",
                        target="ACCEPT",
                        matches=(("udp", (("dport", ("53",)),)),),
                        target_parameters=(),
                    ),
                    # DNS goes over TCP sometimes, too!
                    iptables.Rule(
                        protocol="tcp",
                        src="0.0.0.0/0.0.0.0",
                        dst=f"{dns_server}/255.255.255.255",
                        target="ACCEPT",
                        matches=(("tcp", (("dport", ("53",)),)),),
                        target_parameters=(),
                    ),
                )
                for dns_server in _dns_servers()
            )
        ),
    )
示例#7
0
def _default_rules(conf, log_prefix):
    log_rule = iptables.Rule(protocol='ip',
                             src='0.0.0.0/0.0.0.0',
                             dst='0.0.0.0/0.0.0.0',
                             target='LOG',
                             target_parameters=(('log-prefix',
                                                 (log_prefix, )), ),
                             matches=(('limit', (
                                 ('limit', ('1/sec', )),
                                 ('limit-burst', ('1', )),
                             )), ))

    policy = conf.get_outbound_firewall()
    if policy == 'block':
        return (iptables.Rule(
            protocol='ip',
            src='0.0.0.0/0.0.0.0',
            dst='0.0.0.0/0.0.0.0',
            target='REJECT',
            matches=(),
            target_parameters=((('reject-with',
                                 ('icmp-port-unreachable', ))), ),
        ), log_rule)
    elif policy == 'monitor':
        return (log_rule, )
    else:
        raise AssertionError(policy)
def _well_known_rules(conf):
    # Allow access to certain resources for all services by default.
    yield iptables.Rule(
        protocol="ip",
        src="0.0.0.0/0.0.0.0",
        dst="0.0.0.0/0.0.0.0",
        target="PAASTA-COMMON",
        matches=(),
        target_parameters=(),
    )

    for dep in conf.get_dependencies() or ():
        resource = dep.get("well-known")
        if resource == "internet":
            yield iptables.Rule(
                protocol="ip",
                src="0.0.0.0/0.0.0.0",
                dst="0.0.0.0/0.0.0.0",
                target="PAASTA-INTERNET",
                matches=(),
                target_parameters=(),
            )
        elif resource is not None:
            # TODO: handle better
            raise AssertionError(resource)
def _default_rules(conf, log_prefix):
    log_rule = iptables.Rule(
        protocol="ip",
        src="0.0.0.0/0.0.0.0",
        dst="0.0.0.0/0.0.0.0",
        target="LOG",
        target_parameters=(("log-prefix", (log_prefix, )), ),
        matches=(("limit", (("limit", ("1/sec", )), ("limit-burst",
                                                     ("1", )))), ),
    )

    policy = conf.get_outbound_firewall()
    if policy == "block":
        return (
            iptables.Rule(
                protocol="ip",
                src="0.0.0.0/0.0.0.0",
                dst="0.0.0.0/0.0.0.0",
                target="REJECT",
                matches=(),
                target_parameters=((("reject-with",
                                     ("icmp-port-unreachable", ))), ),
            ),
            log_rule,
        )
    elif policy == "monitor":
        return (log_rule, )
    else:
        raise AssertionError(policy)
示例#10
0
def _cidr_rules(conf):
    for dep in conf.get_dependencies() or ():
        cidr = dep.get('cidr')
        port_str = dep.get('port')

        if cidr is None:
            continue

        try:
            network = ipaddress.IPv4Network(cidr)
        except ipaddress.AddressValueError:
            log.exception(f'Unable to parse IP network: {cidr}')
            continue

        if port_str is not None:
            # port can be either a single port like "443" or a range like "1024:65535"
            ports = str(port_str).split(':')

            if len(ports) > 2:
                log.error(
                    f'"port" must be either a single value or a range like "1024:65535": {port_str}'
                )
                continue

            if not _ports_valid(ports):
                continue

        # Set up an ip rule if no port, or a tcp/udp rule if there is a port
        dst = f'{network.network_address.exploded}/{network.netmask}'
        if port_str is None:
            yield iptables.Rule(
                protocol='ip',
                src='0.0.0.0/0.0.0.0',
                dst=dst,
                target='ACCEPT',
                matches=((
                    'comment',
                    (('comment', (f'allow {network}:*', )), ),
                ), ),
                target_parameters=(),
            )
        else:
            for proto in ('tcp', 'udp'):
                yield iptables.Rule(
                    protocol=proto,
                    src='0.0.0.0/0.0.0.0',
                    dst=dst,
                    target='ACCEPT',
                    matches=(
                        (
                            'comment',
                            (('comment', (f'allow {network}:{port_str}', )), ),
                        ),
                        (
                            proto,
                            (('dport', (str(port_str), )), ),
                        ),
                    ),
                    target_parameters=(),
                )
def _cidr_rules(conf):
    for dep in conf.get_dependencies() or ():
        cidr = dep.get("cidr")
        port_str = dep.get("port")

        if cidr is None:
            continue

        try:
            network = ipaddress.IPv4Network(cidr)
        except ipaddress.AddressValueError:
            log.exception(f"Unable to parse IP network: {cidr}")
            continue

        if port_str is not None:
            # port can be either a single port like "443" or a range like "1024:65535"
            ports = str(port_str).split(":")

            if len(ports) > 2:
                log.error(
                    f'"port" must be either a single value or a range like "1024:65535": {port_str}'
                )
                continue

            if not _ports_valid(ports):
                continue

        # Set up an ip rule if no port, or a tcp/udp rule if there is a port
        dst = f"{network.network_address.exploded}/{network.netmask}"
        if port_str is None:
            yield iptables.Rule(
                protocol="ip",
                src="0.0.0.0/0.0.0.0",
                dst=dst,
                target="ACCEPT",
                matches=(("comment", (("comment",
                                       (f"allow {network}:*", )), )), ),
                target_parameters=(),
            )
        else:
            for proto in ("tcp", "udp"):
                yield iptables.Rule(
                    protocol=proto,
                    src="0.0.0.0/0.0.0.0",
                    dst=dst,
                    target="ACCEPT",
                    matches=(
                        ("comment", (("comment",
                                      (f"allow {network}:{port_str}", )), )),
                        (proto, (("dport", (str(port_str), )), )),
                    ),
                    target_parameters=(),
                )
示例#12
0
def ensure_internet_chain():
    iptables.ensure_chain('PAASTA-INTERNET', (iptables.Rule(
        protocol='ip',
        src='0.0.0.0/0.0.0.0',
        dst='0.0.0.0/0.0.0.0',
        target='ACCEPT',
        matches=(),
    ), ) + tuple(
        iptables.Rule(
            protocol='ip',
            src='0.0.0.0/0.0.0.0',
            dst=ip_range,
            target='RETURN',
            matches=(),
        ) for ip_range in PRIVATE_IP_RANGES))
def _smartstack_rules(conf, soa_dir, synapse_service_dir):
    for dep in conf.get_dependencies() or ():
        namespace = dep.get("smartstack")
        if namespace is None:
            continue

        # TODO: support wildcards

        # synapse backends
        try:
            backends = _synapse_backends(synapse_service_dir, namespace)
        except (OSError, IOError, ValueError):
            # Don't fatal if something goes wrong loading the synapse files
            log.exception(f"Unable to load backend {namespace}")
            backends = ()

        for backend in backends:
            yield iptables.Rule(
                protocol="tcp",
                src="0.0.0.0/0.0.0.0",
                dst="{}/255.255.255.255".format(backend["host"]),
                target="ACCEPT",
                matches=(
                    ("comment", (("comment", ("backend " + namespace, )), )),
                    ("tcp", (("dport", (str(backend["port"]), )), )),
                ),
                target_parameters=(),
            )

        # synapse-haproxy proxy_port
        service, _ = namespace.split(".", 1)
        service_namespaces = get_all_namespaces_for_service(service,
                                                            soa_dir=soa_dir)
        port = dict(service_namespaces)[namespace]["proxy_port"]
        yield _yocalhost_rule(port, "proxy_port " + namespace)
示例#14
0
def _smartstack_rules(conf, soa_dir, synapse_service_dir):
    for dep in conf.get_dependencies():
        namespace = dep.get('smartstack')
        if namespace is None:
            continue

        # TODO: support wildcards

        # synapse backends
        try:
            backends = _synapse_backends(synapse_service_dir, namespace)
        except (OSError, IOError, ValueError):
            # Don't fatal if something goes wrong loading the synapse files
            log.exception('Unable to load backend {}'.format(namespace))
            backends = ()

        for backend in backends:
            yield iptables.Rule(
                protocol='tcp',
                src='0.0.0.0/0.0.0.0',
                dst='{}/255.255.255.255'.format(backend['host']),
                target='ACCEPT',
                matches=(
                    ('comment', (('comment', ('backend ' + namespace, )), )),
                    ('tcp', (('dport', (six.text_type(backend['port']), )), )),
                ),
                target_parameters=(),
            )

        # synapse-haproxy proxy_port
        service, _ = namespace.split('.', 1)
        service_namespaces = get_all_namespaces_for_service(service,
                                                            soa_dir=soa_dir)
        port = dict(service_namespaces)[namespace]['proxy_port']
        yield _yocalhost_rule(port, 'proxy_port ' + namespace)
def dispatch_rule(chain, mac):
    return iptables.Rule(
        protocol="ip",
        src="0.0.0.0/0.0.0.0",
        dst="0.0.0.0/0.0.0.0",
        target=chain,
        matches=(("mac", (("mac-source", (mac.upper(), )), )), ),
        target_parameters=(),
    )
示例#16
0
def dispatch_rule(chain, mac):
    return iptables.Rule(
        protocol='ip',
        src='0.0.0.0/0.0.0.0',
        dst='0.0.0.0/0.0.0.0',
        target=chain,
        matches=(('mac', (('mac-source', (mac.upper(), )), )), ),
        target_parameters=(),
    )
示例#17
0
def ensure_dispatch_chains(service_chains):
    paasta_rules = set(
        itertools.chain.from_iterable((iptables.Rule(
            protocol='ip',
            src='0.0.0.0/0.0.0.0',
            dst='0.0.0.0/0.0.0.0',
            target=chain,
            matches=(('mac', (('mac_source', mac.upper()), )), ),
        ) for mac in macs) for chain, macs in service_chains.items()))
    iptables.ensure_chain('PAASTA', paasta_rules)

    jump_to_paasta = iptables.Rule(
        protocol='ip',
        src='0.0.0.0/0.0.0.0',
        dst='0.0.0.0/0.0.0.0',
        target='PAASTA',
        matches=(),
    )
    iptables.ensure_rule('INPUT', jump_to_paasta)
    iptables.ensure_rule('FORWARD', jump_to_paasta)
示例#18
0
def _default_rule(conf):
    policy = conf.get_outbound_firewall()
    if policy == 'block':
        return iptables.Rule(
            protocol='ip',
            src='0.0.0.0/0.0.0.0',
            dst='0.0.0.0/0.0.0.0',
            target='REJECT',
            matches=(),
        )
    elif policy == 'monitor':
        # TODO: log-prefix
        return iptables.Rule(
            protocol='ip',
            src='0.0.0.0/0.0.0.0',
            dst='0.0.0.0/0.0.0.0',
            target='LOG',
            matches=(),
        )
    else:
        raise AssertionError(policy)
示例#19
0
def _yocalhost_rule(port, comment, protocol='tcp'):
    """Return an iptables rule allowing access to a yocalhost port."""
    return iptables.Rule(
        protocol=protocol,
        src='0.0.0.0/0.0.0.0',
        dst='169.254.255.254/255.255.255.255',
        target='ACCEPT',
        matches=(
            ('comment', (('comment', (comment, )), )),
            (protocol, (('dport', (six.text_type(port), )), )),
        ),
        target_parameters=(),
    )
def _yocalhost_rule(port, comment, protocol="tcp"):
    """Return an iptables rule allowing access to a yocalhost port."""
    return iptables.Rule(
        protocol=protocol,
        src="0.0.0.0/0.0.0.0",
        dst="169.254.255.254/255.255.255.255",
        target="ACCEPT",
        matches=(
            ("comment", (("comment", (comment, )), )),
            (protocol, (("dport", (str(port), )), )),
        ),
        target_parameters=(),
    )
示例#21
0
def _well_known_rules(conf):
    for resource in conf.get_dependencies().get('well-known', ()):
        if resource == 'internet':
            yield iptables.Rule(
                protocol='ip',
                src='0.0.0.0/0.0.0.0',
                dst='0.0.0.0/0.0.0.0',
                target='PAASTA-INTERNET',
                matches=(),
            )
        else:
            # TODO: handle better
            raise AssertionError(resource)
示例#22
0
def _reject_remaining_inbound_traffic_rule(port, protocol="tcp"):
    """Return an iptables rule denying all other traffic.

    This should eventually be turned into a deny-allow,
    but is opt-in at the service level for now."""
    return iptables.Rule(
        protocol=protocol,
        src="0.0.0.0/0.0.0.0",
        dst="0.0.0.0/0.0.0.0",
        target="REJECT",
        matches=((protocol, (("dport", (str(port), )), )), ),
        target_parameters=((("reject-with", ("icmp-port-unreachable", ))), ),
    )
示例#23
0
def _ensure_dns_chain():
    iptables.ensure_chain(
        'PAASTA-DNS',
        tuple(
            itertools.chain.from_iterable((
                iptables.Rule(
                    protocol='udp',
                    src='0.0.0.0/0.0.0.0',
                    dst='{}/255.255.255.255'.format(dns_server),
                    target='ACCEPT',
                    matches=(('udp', (('dport', ('53', )), )), ),
                    target_parameters=(),
                ),
                # DNS goes over TCP sometimes, too!
                iptables.Rule(
                    protocol='tcp',
                    src='0.0.0.0/0.0.0.0',
                    dst='{}/255.255.255.255'.format(dns_server),
                    target='ACCEPT',
                    matches=(('tcp', (('dport', ('53', )), )), ),
                    target_parameters=(),
                ),
            ) for dns_server in _dns_servers())))
示例#24
0
def _smartstack_rules(conf, soa_dir):
    for namespace in conf.get_dependencies().get('smartstack', ()):
        # TODO: handle non-synapse-haproxy services
        # TODO: support wildcards?
        service, _ = namespace.split('.', 1)
        service_namespaces = get_all_namespaces_for_service(service,
                                                            soa_dir=soa_dir)
        port = dict(service_namespaces)[namespace]['proxy_port']

        yield iptables.Rule(protocol='tcp',
                            src='0.0.0.0/0.0.0.0',
                            dst='169.254.255.254/255.255.255.255',
                            target='ACCEPT',
                            matches=(('tcp', (('dport',
                                               six.text_type(port)), )), ))
def ensure_dispatch_chains(service_chains):
    paasta_rules = set(
        itertools.chain.from_iterable(
            (dispatch_rule(chain, mac) for mac in macs)
            for chain, macs in service_chains.items()))
    iptables.ensure_chain("PAASTA", paasta_rules)

    jump_to_paasta = iptables.Rule(
        protocol="ip",
        src="0.0.0.0/0.0.0.0",
        dst="0.0.0.0/0.0.0.0",
        target="PAASTA",
        matches=(),
        target_parameters=(),
    )
    iptables.ensure_rule("INPUT", jump_to_paasta)
    iptables.ensure_rule("FORWARD", jump_to_paasta)
示例#26
0
from collections import namedtuple

import iptc
import mock
import pytest

from paasta_tools import iptables


EMPTY_RULE = iptables.Rule(
    protocol='ip',
    src='0.0.0.0/0.0.0.0',
    dst='0.0.0.0/0.0.0.0',
    target=None,
    matches=(),
    target_parameters=(),
)


@pytest.yield_fixture
def mock_Table():
    with mock.patch.object(
        iptc, 'Table', autospec=True,
    ) as m:
        m.return_value.autocommit = True
        yield m


@pytest.yield_fixture
def mock_Chain():
    with mock.patch.object(
示例#27
0
from collections import namedtuple

import iptc
import mock
import pytest

from paasta_tools import iptables

EMPTY_RULE = iptables.Rule(
    protocol="ip",
    src="0.0.0.0/0.0.0.0",
    dst="0.0.0.0/0.0.0.0",
    target=None,
    matches=(),
    target_parameters=(),
)


@pytest.yield_fixture
def mock_Table():
    with mock.patch.object(iptc, "Table", autospec=True) as m:
        m.return_value.autocommit = True
        yield m


@pytest.yield_fixture
def mock_Chain():
    with mock.patch.object(iptc, "Chain", autospec=True) as m:
        yield m