Beispiel #1
0
    def test_add_ip_set(self):
        """Test addition of IP to a given set"""
        # Disable W0212: Test access protected members of admin module.
        # pylint: disable=W0212
        iptables.add_ip_set('foo', '1.2.3.4')

        treadmill.iptables._ipset.assert_called_with('add', 'foo', '1.2.3.4')
Beispiel #2
0
 def on_created(path):
     """Invoked when a network rule is created."""
     rule_file = os.path.basename(path)
     _LOGGER.info('adding %r', rule_file)
     # The rule is the filename
     chain_rule = rulemgr.get_rule(rule_file)
     if chain_rule is not None:
         chain, rule = chain_rule
         iptables.add_rule(rule, chain=chain)
         if isinstance(rule, fw.PassThroughRule):
             passthrough[rule.src_ip] = (
                 passthrough.setdefault(rule.src_ip, 0) + 1
             )
             _LOGGER.info('Adding passthrough %r', rule.src_ip)
             iptables.add_ip_set(iptables.SET_PASSTHROUGHS, rule.src_ip)
             iptables.flush_pt_conntrack_table(rule.src_ip)
         elif isinstance(rule, (fw.SNATRule, fw.DNATRule)):
             if rule.proto == 'udp':
                 iptables.flush_conntrack_table(
                     src_ip=rule.src_ip,
                     src_port=rule.src_port,
                     dst_ip=rule.dst_ip,
                     dst_port=rule.dst_port,
                 )
     else:
         _LOGGER.warning('Ignoring unparseable rule %r', rule_file)
Beispiel #3
0
    def test_add_ip_set(self):
        """Test addition of IP to a given set"""
        # Disable protected-access: Test access protected members .
        # pylint: disable=protected-access
        iptables.add_ip_set('foo', '1.2.3.4')

        treadmill.iptables._ipset.assert_called_with('-exist', 'add', 'foo',
                                                     '1.2.3.4')
Beispiel #4
0
def _add_mark_rule(src_ip, environment):
    """Add an environment mark for all traffic coming from an IP.

    :param ``str`` src_ip:
        Source IP to be marked
    :param ``str`` environment:
        Environment to use for the mark
    """
    assert environment in _SET_BY_ENVIRONMENT, \
        'Unknown environment: %r' % environment

    target_set = _SET_BY_ENVIRONMENT[environment]
    iptables.add_ip_set(target_set, src_ip)

    # Check that the IP is not marked in any other environment
    other_env_sets = {
        env_set for env_set in six.viewvalues(_SET_BY_ENVIRONMENT)
        if env_set != target_set
    }
    for other_set in other_env_sets:
        if iptables.test_ip_set(other_set, src_ip) is True:
            raise Exception('%r is already in %r' % (src_ip, other_set))
Beispiel #5
0
def _watcher(root_dir, rules_dir, containers_dir, watchdogs_dir):
    """Treadmill Firewall rule watcher.
    """
    rules_dir = os.path.join(root_dir, rules_dir)
    containers_dir = os.path.join(root_dir, containers_dir)
    watchdogs_dir = os.path.join(root_dir, watchdogs_dir)

    # Setup the watchdog
    watchdogs = watchdog.Watchdog(watchdogs_dir)
    wd = watchdogs.create(
        'svc-{svc_name}'.format(svc_name='firewall_watcher'),
        '{hb:d}s'.format(hb=_FW_WATCHER_HEARTBEAT * 2),
        'Service firewall watcher failed'
    )

    rulemgr = rulefile.RuleMgr(rules_dir, containers_dir)
    passthrough = {}

    def on_created(path):
        """Invoked when a network rule is created."""
        rule_file = os.path.basename(path)
        _LOGGER.info('adding %r', rule_file)
        # The rule is the filename
        chain_rule = rulemgr.get_rule(rule_file)
        if chain_rule is not None:
            chain, rule = chain_rule
            iptables.add_rule(rule, chain=chain)
            if isinstance(rule, fw.PassThroughRule):
                passthrough[rule.src_ip] = (
                    passthrough.setdefault(rule.src_ip, 0) + 1
                )
                _LOGGER.info('Adding passthrough %r', rule.src_ip)
                iptables.add_ip_set(iptables.SET_PASSTHROUGHS, rule.src_ip)
                iptables.flush_pt_conntrack_table(rule.src_ip)
        else:
            _LOGGER.warning('Ignoring unparseable rule %r', rule_file)

    def on_deleted(path):
        """Invoked when a network rule is deleted."""
        # Edge case, if the directory where the rules are kept gets removed,
        # abort
        if path == rulemgr.path:
            _LOGGER.critical('Network rules directory was removed: %r',
                             path)
            utils.sys_exit(1)

        # The rule is the filename
        rule_file = os.path.basename(path)
        _LOGGER.info('Removing %r', rule_file)
        chain_rule = rulemgr.get_rule(rule_file)
        if chain_rule is not None:
            chain, rule = chain_rule
            iptables.delete_rule(rule, chain=chain)
            if isinstance(rule, fw.PassThroughRule):
                if passthrough[rule.src_ip] == 1:
                    # Remove the IPs from the passthrough set
                    passthrough.pop(rule.src_ip)
                    _LOGGER.info('Removing passthrough %r', rule.src_ip)
                    iptables.rm_ip_set(iptables.SET_PASSTHROUGHS, rule.src_ip)
                    iptables.flush_pt_conntrack_table(rule.src_ip)
                else:
                    passthrough[rule.src_ip] -= 1

        else:
            _LOGGER.warning('Ignoring unparseable file %r', rule_file)

    _LOGGER.info('Monitoring fw rules changes in %r', rulemgr.path)
    watch = dirwatch.DirWatcher(rulemgr.path)
    watch.on_created = on_created
    watch.on_deleted = on_deleted

    # Minimal initialization of the all chains and sets
    _init_rules()

    # now that we are watching, prime the rules
    current_rules = rulemgr.get_rules()

    # Bulk apply rules
    _configure_rules(current_rules)
    for _chain, rule in current_rules:
        if isinstance(rule, fw.PassThroughRule):
            passthrough[rule.src_ip] = (
                passthrough.setdefault(rule.src_ip, 0) + 1
            )
            # Add the IPs to the passthrough set
            _LOGGER.info('Adding passthrough %r', rule.src_ip)
            iptables.add_ip_set(iptables.SET_PASSTHROUGHS, rule.src_ip)

    _LOGGER.info('Current rules: %r', current_rules)
    while True:
        if watch.wait_for_events(timeout=_FW_WATCHER_HEARTBEAT):
            # Process no more than 5 events between heartbeats
            watch.process_events(max_events=5)

        rulemgr.garbage_collect()
        wd.heartbeat()

    _LOGGER.info('service shutdown.')
    wd.remove()
Beispiel #6
0
def _unshare_network(tm_env, container_dir, app):
    """Configures private app network.

    :param ``appenv.AppEnvironment`` tm_env:
        Treadmill application environment
    """
    unique_name = appcfg.app_unique_name(app)
    # Configure DNAT rules while on host network.
    for endpoint in app.endpoints:
        _LOGGER.info('Creating DNAT rule: %s:%s -> %s:%s',
                     app.network.external_ip,
                     endpoint.real_port,
                     app.network.vip,
                     endpoint.port)
        dnatrule = firewall.DNATRule(proto=endpoint.proto,
                                     dst_ip=app.network.external_ip,
                                     dst_port=endpoint.real_port,
                                     new_ip=app.network.vip,
                                     new_port=endpoint.port)
        snatrule = firewall.SNATRule(proto=endpoint.proto,
                                     src_ip=app.network.vip,
                                     src_port=endpoint.port,
                                     new_ip=app.network.external_ip,
                                     new_port=endpoint.real_port)
        tm_env.rules.create_rule(chain=iptables.PREROUTING_DNAT,
                                 rule=dnatrule,
                                 owner=unique_name)
        tm_env.rules.create_rule(chain=iptables.POSTROUTING_SNAT,
                                 rule=snatrule,
                                 owner=unique_name)

        # See if this container requires vring service
        if app.vring:
            _LOGGER.debug('adding %r to VRing set', app.network.vip)
            iptables.add_ip_set(
                iptables.SET_VRING_CONTAINERS,
                app.network.vip
            )

        # See if this was an "infra" endpoint and if so add it to the whitelist
        # set.
        if getattr(endpoint, 'type', None) == 'infra':
            _LOGGER.debug('adding %s:%s to infra services set',
                          app.network.vip, endpoint.port)
            iptables.add_ip_set(
                iptables.SET_INFRA_SVC,
                '{ip},{proto}:{port}'.format(
                    ip=app.network.vip,
                    proto=endpoint.proto,
                    port=endpoint.port,
                )
            )

    for port in app.ephemeral_ports.tcp:
        _LOGGER.info('Creating ephemeral DNAT rule: %s:%s -> %s:%s',
                     app.network.external_ip, port,
                     app.network.vip, port)
        dnatrule = firewall.DNATRule(proto='tcp',
                                     dst_ip=app.network.external_ip,
                                     dst_port=port,
                                     new_ip=app.network.vip,
                                     new_port=port)
        tm_env.rules.create_rule(chain=iptables.PREROUTING_DNAT,
                                 rule=dnatrule,
                                 owner=unique_name)
        # Treat ephemeral ports as infra, consistent with current prodperim
        # behavior.
        iptables.add_ip_set(iptables.SET_INFRA_SVC,
                            '{ip},tcp:{port}'.format(ip=app.network.vip,
                                                     port=port))

    for port in app.ephemeral_ports.udp:
        _LOGGER.info('Creating ephemeral DNAT rule: %s:%s -> %s:%s',
                     app.network.external_ip, port,
                     app.network.vip, port)
        dnatrule = firewall.DNATRule(proto='udp',
                                     dst_ip=app.network.external_ip,
                                     dst_port=port,
                                     new_ip=app.network.vip,
                                     new_port=port)
        tm_env.rules.create_rule(chain=iptables.PREROUTING_DNAT,
                                 rule=dnatrule,
                                 owner=unique_name)
        # Treat ephemeral ports as infra, consistent with current prodperim
        # behavior.
        iptables.add_ip_set(iptables.SET_INFRA_SVC,
                            '{ip},udp:{port}'.format(ip=app.network.vip,
                                                     port=port))

    # configure passthrough while on main network.
    if getattr(app, 'passthrough', None):
        _LOGGER.info('adding passthrough for: %r', app.passthrough)
        # Resolve all the hosts (+dedup)
        new_ips = {
            socket.gethostbyname(host)
            for host in app.passthrough
        }

        # Create a passthrough rule from each of the source IP to the
        # container IP and record these source IP in a set.
        for ipaddr in new_ips:
            passthroughrule = firewall.PassThroughRule(
                src_ip=ipaddr,
                dst_ip=app.network.vip,
            )
            tm_env.rules.create_rule(chain=iptables.PREROUTING_PASSTHROUGH,
                                     rule=passthroughrule,
                                     owner=unique_name)

    # configure exception filter rules
    try:
        firewall_plugin = plugin_manager.load(
            'treadmill.firewall.plugins', 'firewall'
        )
        firewall_plugin.apply_exception_rules(tm_env, container_dir, app)
    except Exception:  # pylint: disable=W0703
        _LOGGER.exception(
            'Error in firewall plugin, skip applying firewall exception rules.'
        )

    service_ip = None
    if app.shared_ip:
        service_ip = app.network.external_ip

    # Unshare network and create virtual device
    newnet.create_newnet(app.network.veth,
                         app.network.vip,
                         app.network.gateway,
                         service_ip)