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')
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)
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')
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))
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()
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)