def test_snat_extra_rule(self): """Tests SNAT setup when rule needs to be removed. """ # Disable protected-access: Test access protected members. # pylint: disable=protected-access treadmill.iptables._get_current_snat_rules.return_value = ( self.snat_rules | set([ firewall.SNATRule('tcp', '172.31.81.67', 5004, '192.168.2.15', 22), ]) ) desired_rules = ( self.snat_rules ) iptables.configure_snat_rules( desired_rules, iptables.PREROUTING_DNAT ) self.assertEqual(0, treadmill.iptables.add_snat_rule.call_count) treadmill.iptables.delete_snat_rule.assert_called_with( firewall.SNATRule('tcp', '172.31.81.67', 5004, '192.168.2.15', 22), chain=iptables.PREROUTING_DNAT )
def test_snat_missing_rule(self): """Tests DNAT setup when new rule needs to be created. """ # Disable W0212: Test access protected members. # pylint: disable=W0212 treadmill.iptables._get_current_snat_rules.return_value = \ self.snat_rules desired_rules = ( self.snat_rules | set([ firewall.SNATRule('tcp', '172.31.81.67', 5004, '192.168.2.15', 22), ]) ) iptables.configure_snat_rules( desired_rules, iptables.POSTROUTING_SNAT ) treadmill.iptables.add_snat_rule.assert_called_with( firewall.SNATRule('tcp', '172.31.81.67', 5004, '192.168.2.15', 22), chain=iptables.POSTROUTING_SNAT ) self.assertEquals(0, treadmill.iptables.delete_snat_rule.call_count)
def test_dnat_missing_rule(self): """Tests DNAT setup when new rule needs to be created.""" treadmill.iptables.get_current_nat_rules.return_value = \ self.dnat_rules | self.snat_rules desired_rules = ( self.dnat_rules | self.snat_rules | set([ firewall.DNATRule('tcp', '172.31.81.67', 5004, '192.168.2.15', 22), firewall.SNATRule('tcp', '172.31.81.67', 5004, '192.168.2.15', 22), ]) ) iptables.configure_nat_rules( desired_rules, iptables.PREROUTING_DNAT ) treadmill.iptables.add_dnat_rule.assert_called_with( firewall.DNATRule('tcp', '172.31.81.67', 5004, '192.168.2.15', 22), chain=iptables.PREROUTING_DNAT ) treadmill.iptables.add_snat_rule.assert_called_with( firewall.SNATRule('tcp', '172.31.81.67', 5004, '192.168.2.15', 22), chain=iptables.PREROUTING_DNAT ) self.assertEquals(0, treadmill.iptables.delete_dnat_rule.call_count) self.assertEquals(0, treadmill.iptables.delete_snat_rule.call_count)
def setUp(self): # Note: These two match the content of NAT_TABLE_SAVE self.dnat_rules = set([ firewall.DNATRule(proto='udp', dst_ip='172.31.81.67', dst_port=5002, new_ip='192.168.1.13', new_port=8000), firewall.DNATRule(proto='tcp', dst_ip='172.31.81.67', dst_port=5000, new_ip='192.168.0.11', new_port=8000), firewall.DNATRule(proto='tcp', dst_ip='172.31.81.67', dst_port=5003, new_ip='192.168.1.13', new_port=22), firewall.DNATRule(proto='tcp', dst_ip='172.31.81.67', dst_port=5001, new_ip='192.168.0.11', new_port=22), ]) self.snat_rules = set([ firewall.SNATRule(proto='udp', src_ip='192.168.0.3', src_port=22, new_ip='172.31.81.67', new_port=5001), ]) self.passthrough_rules = set([ firewall.PassThroughRule(src_ip='10.197.19.18', dst_ip='192.168.3.2'), firewall.PassThroughRule(src_ip='10.197.19.19', dst_ip='192.168.2.2'), ]) self.iptables_state = _test_data('iptables_state.save') self.iptables_empty_state = _test_data('iptables_empty_state.save') self.iptables_filter_state = _test_data('iptables_filter_state.save') self.ipset_state = _test_data('ipset_state.save') self.nat_table_save = _test_data('iptables_test_nat_table.save')
def setUp(self): # Note: These two match the content of NAT_TABLE_SAVE self.dnat_rules = set([ firewall.DNATRule('udp', '172.31.81.67', 5002, '192.168.1.13', 8000), firewall.DNATRule('tcp', '172.31.81.67', 5000, '192.168.0.11', 8000), firewall.DNATRule('tcp', '172.31.81.67', 5003, '192.168.1.13', 22), firewall.DNATRule('tcp', '172.31.81.67', 5001, '192.168.0.11', 22), ]) self.snat_rules = set([ firewall.SNATRule('udp', '192.168.0.3', 22, '172.31.81.67', 5001), ]) self.passthrough_rules = set([ firewall.PassThroughRule(src_ip='10.197.19.18', dst_ip='192.168.3.2'), firewall.PassThroughRule(src_ip='10.197.19.19', dst_ip='192.168.2.2'), ])
def _get_current_snat_rules(chain): """Extract all SNAT rules in chain from iptables. :param ``str`` chain: Iptables chain to process. :returns: ``set([SNATRule])`` -- Set of rules. """ if chain is None: chain = POSTROUTING_SNAT rules = set() for line in _iptables_output('nat', '-S', chain).splitlines(): snat_match = _SNAT_RULE_RE.match(line.strip()) if snat_match: data = snat_match.groupdict() rule = firewall.SNATRule(proto=data['proto'], src_ip=data['src_ip'], src_port=data['src_port'], dst_ip=data['dst_ip'], dst_port=data['dst_port'], new_ip=data['new_ip'], new_port=data['new_port']) rules.add(rule) continue return rules
def setUp(self): # Pylint warning re accessing protected class member. # pylint: disable=W0212 self.root = tempfile.mkdtemp() self.rules_dir = os.path.join(self.root, 'rules') self.apps_dir = os.path.join(self.root, 'apps') os.makedirs(self.rules_dir) os.makedirs(self.apps_dir) self.rules = rulefile.RuleMgr(self.rules_dir, self.apps_dir) self.tcpdnatrule = firewall.DNATRule( proto='tcp', dst_ip='1.1.1.1', dst_port=123, new_ip='2.2.2.2', new_port=234 ) self.tcpdnatfile = rulefile.RuleMgr._filenameify( 'SOME_CHAIN', self.tcpdnatrule ) self.tcpdnatuid = '1234' with io.open(os.path.join(self.apps_dir, self.tcpdnatuid), 'w'): pass self.udpdnatrule = firewall.DNATRule( proto='udp', dst_ip='1.1.1.1', dst_port=123, new_ip='2.2.2.2', new_port=234 ) self.udpdnatfile = rulefile.RuleMgr._filenameify( 'SOME_CHAIN', self.udpdnatrule ) self.udpdnatuid = '2345' with io.open(os.path.join(self.apps_dir, self.udpdnatuid), 'w'): pass self.udpsnatrule = firewall.SNATRule( proto='udp', src_ip='1.1.1.1', src_port=123, new_ip='2.2.2.2', new_port=234 ) self.udpsnatfile = rulefile.RuleMgr._filenameify( 'SOME_CHAIN', self.udpsnatrule ) self.udpsnatuid = '3456' with io.open(os.path.join(self.apps_dir, self.udpsnatuid), 'w'): pass self.passthroughrule = firewall.PassThroughRule('3.3.3.3', '4.4.4.4') self.passthroughfile = rulefile.RuleMgr._filenameify( 'SOME_CHAIN', self.passthroughrule, ) self.passthroughuid = '4321' with io.open(os.path.join(self.apps_dir, self.passthroughuid), 'w'): pass
def setUp(self): # Note: These two match the content of NAT_TABLE_SAVE self.dnat_rules = set([ firewall.DNATRule(proto='udp', dst_ip='172.31.81.67', dst_port=5002, new_ip='192.168.1.13', new_port=8000), firewall.DNATRule(proto='tcp', dst_ip='172.31.81.67', dst_port=5000, new_ip='192.168.0.11', new_port=8000), firewall.DNATRule(proto='tcp', dst_ip='172.31.81.67', dst_port=5003, new_ip='192.168.1.13', new_port=22), firewall.DNATRule(proto='tcp', dst_ip='172.31.81.67', dst_port=5001, new_ip='192.168.0.11', new_port=22), ]) self.snat_rules = set([ firewall.SNATRule(proto='udp', src_ip='192.168.0.3', src_port=22, new_ip='172.31.81.67', new_port=5001), ]) self.passthrough_rules = set([ firewall.PassThroughRule(src_ip='10.197.19.18', dst_ip='192.168.3.2'), firewall.PassThroughRule(src_ip='10.197.19.19', dst_ip='192.168.2.2'), ]) with io.open(self.IPTABLES_STATE) as f: self.iptables_state = f.read() with io.open(self.IPTABLES_EMPTY_STATE) as f: self.iptables_empty_state = f.read() with io.open(self.IPTABLES_FILTER_STATE) as f: self.iptables_filter_state = f.read() with io.open(self.IPTABLES_FILTER_DROP_STATE) as f: self.iptables_filter_drop_state = f.read() with io.open(self.IPSET_STATE) as f: self.ipset_state = f.read() with io.open(self.NAT_TABLE_SAVE) as f: self.nat_table_save = f.read()
def test_delete_snat_rule(self): """Test snat rule deletion.""" iptables.delete_snat_rule(firewall.SNATRule('tcp', '1.1.1.1', 123, '2.2.2.2', 345), 'SOME_RULE') treadmill.iptables.delete_raw_rule.assert_called_with( 'nat', 'SOME_RULE', ('-d 1.1.1.1 -p tcp -m tcp --dport 123' ' -j SNAT --to 2.2.2.2:345') )
def test_delete_snat_rule(self): """Test snat rule deletion.""" iptables.delete_snat_rule( firewall.SNATRule(proto='tcp', src_ip='1.1.1.1', src_port=123, new_ip='2.2.2.2', new_port=345), 'SOME_RULE') treadmill.iptables.delete_raw_rule.assert_called_with( 'nat', 'SOME_RULE', ('-s 1.1.1.1 -d 0.0.0.0/0 -p tcp -m tcp --sport 123' ' -j SNAT --to-source 2.2.2.2:345'))
def get_rule(rulespec): """Parse a forwarding rule spec into a usable firewall rule. :param ``str`` rulespec: Forward rule in string form :returns: tuple(Chain, ``DNATRule`` | ``SNATRule`` | ``PassThroughRule``) | ``None`` -- A tuple of a chain and a firewall rule object. If parsing failed, returns ``None`` """ match = _DNAT_FILE_RE.match(rulespec) if match: data = match.groupdict() return (data['chain'], firewall.DNATRule( proto=data['proto'], src_ip=(data['src_ip'] if data['src_ip'] != _ANY else None), src_port=(data['src_port'] if data['src_port'] != _ANY else None), dst_ip=(data['dst_ip'] if data['dst_ip'] != _ANY else None), dst_port=(data['dst_port'] if data['dst_port'] != _ANY else None), new_ip=data['new_ip'], new_port=data['new_port'])) match = _SNAT_FILE_RE.match(rulespec) if match: data = match.groupdict() return (data['chain'], firewall.SNATRule( proto=data['proto'], src_ip=(data['src_ip'] if data['src_ip'] != _ANY else None), src_port=(data['src_port'] if data['src_port'] != _ANY else None), dst_ip=(data['dst_ip'] if data['dst_ip'] != _ANY else None), dst_port=(data['dst_port'] if data['dst_port'] != _ANY else None), new_ip=data['new_ip'], new_port=data['new_port'])) match = _PASSTHROUGH_FILE_RE.match(rulespec) if match: data = match.groupdict() return (data['chain'], firewall.PassThroughRule(data['src_ip'], data['dst_ip'])) return None
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)
def test_finish(self): """Tests container finish procedure and freeing of the resources. """ # Access protected module _kill_apps_by_root # pylint: disable=W0212 manifest = { 'app': 'proid.myapp', 'cell': 'test', 'cpu': '100%', 'disk': '100G', 'environment': 'dev', 'memory': '100M', 'name': 'proid.myapp#001', 'proid': 'foo', 'shared_network': False, 'task': '001', 'uniqueid': '0000000ID1234', 'archive': [ '/var/tmp/treadmill' ], 'endpoints': [ { 'port': 8000, 'name': 'http', 'real_port': 5000, 'proto': 'tcp', }, { 'port': 54321, 'type': 'infra', 'name': 'ssh', 'real_port': 54321, 'proto': 'tcp', } ], 'ephemeral_ports': { 'tcp': [45024], 'udp': [62422], }, 'services': [ { 'name': 'web_server', 'command': '/bin/false', 'restart': { 'limit': 3, 'interval': 60, }, } ], 'vring': { 'some': 'settings' } } treadmill.appcfg.manifest.read.return_value = manifest app_unique_name = 'proid.myapp-001-0000000ID1234' mock_cgroup_client = self.tm_env.svc_cgroup.make_client.return_value mock_ld_client = self.tm_env.svc_localdisk.make_client.return_value mock_nwrk_client = self.tm_env.svc_network.make_client.return_value localdisk = { 'block_dev': '/dev/foo', } mock_ld_client.get.return_value = localdisk network = { 'vip': '192.168.0.2', 'gateway': '192.168.254.254', 'veth': 'testveth.0', 'external_ip': '172.31.81.67', } mock_nwrk_client.get.return_value = network app_dir = os.path.join(self.tm_env.apps_dir, app_unique_name) # Create content in app root directory, verify that it is archived. fs.mkdir_safe(os.path.join(app_dir, 'root', 'xxx')) fs.mkdir_safe(os.path.join(app_dir, 'services')) # Simulate daemontools finish script, marking the app is done. with open(os.path.join(app_dir, 'exitinfo'), 'w') as f: f.write(yaml.dump({'service': 'web_server', 'rc': 0, 'sig': 0})) mock_zkclient = kazoo.client.KazooClient() mock_watchdog = mock.Mock() app_finish.finish(self.tm_env, mock_zkclient, app_dir, mock_watchdog) treadmill.subproc.check_call.assert_has_calls( [ mock.call( [ 's6_svc', '-d', app_dir, ] ), mock.call( [ 's6_svwait', '-d', app_dir, ] ), ] ) # All resource service clients are properly created self.tm_env.svc_cgroup.make_client.assert_called_with( os.path.join(app_dir, 'cgroups') ) self.tm_env.svc_localdisk.make_client.assert_called_with( os.path.join(app_dir, 'localdisk') ) self.tm_env.svc_network.make_client.assert_called_with( os.path.join(app_dir, 'network') ) treadmill.runtime.linux._finish._kill_apps_by_root.assert_called_with( os.path.join(app_dir, 'root') ) # Verify that we tested the archiving for the app root volume treadmill.fs.archive_filesystem.assert_called_with( '/dev/foo', os.path.join(app_dir, 'root'), os.path.join(app_dir, '001_xxx.xx.com_20150122_141436537918.tar'), mock.ANY ) # Verify that the file is uploaded by Uploader app = utils.to_obj(manifest) treadmill.runtime.linux._finish._send_container_archive\ .assert_called_with( mock_zkclient, app, os.path.join(app_dir, '001_xxx.xx.com_20150122_141436537918.tar.gz'), ) # Verify that the app folder was deleted self.assertFalse(os.path.exists(app_dir)) # Cleanup the block device mock_ld_client.delete.assert_called_with(app_unique_name) # Cleanup the cgroup resource mock_cgroup_client.delete.assert_called_with(app_unique_name) # Cleanup network resources mock_nwrk_client.get.assert_called_with(app_unique_name) self.tm_env.rules.unlink_rule.assert_has_calls( [ mock.call(chain=iptables.PREROUTING_DNAT, rule=firewall.DNATRule( proto='tcp', dst_ip='172.31.81.67', dst_port=5000, new_ip='192.168.0.2', new_port=8000 ), owner=app_unique_name), mock.call(chain=iptables.POSTROUTING_SNAT, rule=firewall.SNATRule( proto='tcp', src_ip='192.168.0.2', src_port=8000, new_ip='172.31.81.67', new_port=5000 ), owner=app_unique_name), mock.call(chain=iptables.PREROUTING_DNAT, rule=firewall.DNATRule( proto='tcp', dst_ip='172.31.81.67', dst_port=54321, new_ip='192.168.0.2', new_port=54321 ), owner=app_unique_name), mock.call(chain=iptables.POSTROUTING_SNAT, rule=firewall.SNATRule( proto='tcp', src_ip='192.168.0.2', src_port=54321, new_ip='172.31.81.67', new_port=54321 ), owner=app_unique_name), mock.call(chain=iptables.PREROUTING_DNAT, rule=firewall.DNATRule( proto='tcp', dst_ip='172.31.81.67', dst_port=45024, new_ip='192.168.0.2', new_port=45024 ), owner=app_unique_name), mock.call(chain=iptables.PREROUTING_DNAT, rule=firewall.DNATRule( proto='udp', dst_ip='172.31.81.67', dst_port=62422, new_ip='192.168.0.2', new_port=62422 ), owner=app_unique_name), ], any_order=True ) self.assertEqual(self.tm_env.rules.unlink_rule.call_count, 6) treadmill.iptables.rm_ip_set.assert_has_calls( [ mock.call(treadmill.iptables.SET_INFRA_SVC, '192.168.0.2,tcp:54321'), mock.call(treadmill.iptables.SET_INFRA_SVC, '192.168.0.2,tcp:45024'), mock.call(treadmill.iptables.SET_INFRA_SVC, '192.168.0.2,udp:62422'), mock.call(treadmill.iptables.SET_VRING_CONTAINERS, '192.168.0.2'), ], any_order=True ) self.assertEqual(treadmill.iptables.rm_ip_set.call_count, 4) mock_nwrk_client.delete.assert_called_with(app_unique_name) treadmill.appevents.post.assert_called_with( mock.ANY, events.FinishedTraceEvent( instanceid='proid.myapp#001', rc=0, signal=0, payload={ 'service': 'web_server', 'sig': 0, 'rc': 0 } ) ) treadmill.rrdutils.flush_noexc.assert_called_with( os.path.join(self.root, 'metrics', 'apps', app_unique_name + '.rrd') ) shutil.copy.assert_called_with( os.path.join(self.root, 'metrics', 'apps', app_unique_name + '.rrd'), os.path.join(app_dir, 'metrics.rrd') ) self.assertTrue(mock_watchdog.remove.called)
def test_finish(self): """Tests container finish procedure and freeing of the resources. """ manifest = { 'app': 'proid.myapp', 'cell': 'test', 'cpu': '100%', 'disk': '100G', 'environment': 'dev', 'memory': '100M', 'name': 'proid.myapp#001', 'proid': 'foo', 'shared_network': False, 'task': '001', 'uniqueid': '0000000ID1234', 'archive': ['/var/lib/treadmill'], 'endpoints': [{ 'port': 8000, 'name': 'http', 'real_port': 5000, 'proto': 'tcp', }, { 'port': 54321, 'type': 'infra', 'name': 'ssh', 'real_port': 54321, 'proto': 'tcp', }], 'ephemeral_ports': { 'tcp': [45024], 'udp': [62422], }, 'passthrough': [ '8.8.8.8', '9.9.9.9', ], 'services': [{ 'name': 'web_server', 'command': '/bin/false', 'restart': { 'limit': 3, 'interval': 60, }, }], 'vring': { 'some': 'settings' } } treadmill.appcfg.manifest.read.return_value = manifest app_unique_name = 'proid.myapp-001-0000000ID1234' mock_cgroup_client = self.tm_env.svc_cgroup.make_client.return_value mock_ld_client = self.tm_env.svc_localdisk.make_client.return_value mock_nwrk_client = self.tm_env.svc_network.make_client.return_value localdisk = { 'block_dev': '/dev/foo', } mock_ld_client.get.return_value = localdisk network = { 'vip': '192.168.0.2', 'gateway': '192.168.254.254', 'veth': 'testveth.0', 'external_ip': '172.31.81.67', } mock_nwrk_client.get.return_value = network app_dir = os.path.join(self.tm_env.apps_dir, app_unique_name) data_dir = os.path.join(app_dir, 'data') # Create content in app root directory, verify that it is archived. fs.mkdir_safe(os.path.join(data_dir, 'root', 'xxx')) fs.mkdir_safe(os.path.join(data_dir, 'services')) # Simulate daemontools finish script, marking the app is done. with io.open(os.path.join(data_dir, 'exitinfo'), 'w') as f: f.writelines( utils.json_genencode( { 'service': 'web_server', 'return_code': 0, 'signal': 0 }, )) app_finish.finish(self.tm_env, app_dir) # All resource service clients are properly created self.tm_env.svc_cgroup.make_client.assert_called_with( os.path.join(data_dir, 'resources', 'cgroups')) self.tm_env.svc_localdisk.make_client.assert_called_with( os.path.join(data_dir, 'resources', 'localdisk')) self.tm_env.svc_network.make_client.assert_called_with( os.path.join(data_dir, 'resources', 'network')) # Cleanup the block device mock_ld_client.delete.assert_called_with(app_unique_name) # Cleanup the cgroup resource mock_cgroup_client.delete.assert_called_with(app_unique_name) # Cleanup network resources mock_nwrk_client.get.assert_called_with(app_unique_name) self.tm_env.rules.unlink_rule.assert_has_calls([ mock.call(chain=iptables.PREROUTING_DNAT, rule=firewall.DNATRule(proto='tcp', dst_ip='172.31.81.67', dst_port=5000, new_ip='192.168.0.2', new_port=8000), owner=app_unique_name), mock.call(chain=iptables.POSTROUTING_SNAT, rule=firewall.SNATRule(proto='tcp', src_ip='192.168.0.2', src_port=8000, new_ip='172.31.81.67', new_port=5000), owner=app_unique_name), mock.call(chain=iptables.PREROUTING_DNAT, rule=firewall.DNATRule(proto='tcp', dst_ip='172.31.81.67', dst_port=54321, new_ip='192.168.0.2', new_port=54321), owner=app_unique_name), mock.call(chain=iptables.POSTROUTING_SNAT, rule=firewall.SNATRule(proto='tcp', src_ip='192.168.0.2', src_port=54321, new_ip='172.31.81.67', new_port=54321), owner=app_unique_name), mock.call(chain=iptables.PREROUTING_DNAT, rule=firewall.DNATRule(proto='tcp', dst_ip='172.31.81.67', dst_port=45024, new_ip='192.168.0.2', new_port=45024), owner=app_unique_name), mock.call(chain=iptables.PREROUTING_DNAT, rule=firewall.DNATRule(proto='udp', dst_ip='172.31.81.67', dst_port=62422, new_ip='192.168.0.2', new_port=62422), owner=app_unique_name), mock.call(chain=iptables.PREROUTING_PASSTHROUGH, rule=firewall.PassThroughRule( src_ip='8.8.8.8', dst_ip='192.168.0.2', ), owner=app_unique_name), mock.call(chain=iptables.PREROUTING_PASSTHROUGH, rule=firewall.PassThroughRule( src_ip='9.9.9.9', dst_ip='192.168.0.2', ), owner=app_unique_name), ], any_order=True) self.assertEqual(self.tm_env.rules.unlink_rule.call_count, 8) treadmill.iptables.rm_ip_set.assert_has_calls([ mock.call(treadmill.iptables.SET_INFRA_SVC, '192.168.0.2,tcp:54321'), mock.call(treadmill.iptables.SET_INFRA_SVC, '192.168.0.2,tcp:45024'), mock.call(treadmill.iptables.SET_INFRA_SVC, '192.168.0.2,udp:62422'), mock.call(treadmill.iptables.SET_VRING_CONTAINERS, '192.168.0.2'), ], any_order=True) self.assertEqual(treadmill.iptables.rm_ip_set.call_count, 4) mock_nwrk_client.delete.assert_called_with(app_unique_name) treadmill.iptables.flush_cnt_conntrack_table.assert_called_with( '192.168.0.2') treadmill.appevents.post.assert_called_with( mock.ANY, events.FinishedTraceEvent(instanceid='proid.myapp#001', rc=0, signal=0, payload={ 'service': 'web_server', 'signal': 0, 'return_code': 0 })) treadmill.rrdutils.flush_noexc.assert_called_with( os.path.join(self.root, 'metrics', 'apps', app_unique_name + '.rrd')) shutil.copy.assert_called_with( os.path.join(self.root, 'metrics', 'apps', app_unique_name + '.rrd'), os.path.join(data_dir, 'metrics.rrd')) treadmill.runtime.archive_logs.assert_called()
def test__unshare_network_complex(self): """Test unshare network advanced sequence (ephemeral/passthrough).""" # Disable W0212: Access to a protected member # pylint: disable=W0212 app = utils.to_obj({ 'type': 'native', 'name': 'myproid.test#0', 'environment': 'dev', 'uniqueid': 'ID1234', 'network': { 'veth': 'id1234.0', 'vip': '192.168.0.2', 'gateway': '192.168.254.254', 'external_ip': '172.31.81.67', }, 'shared_ip': False, 'endpoints': [{ 'name': 'ssh', 'port': 54321, 'real_port': 54321, 'type': 'infra', 'proto': 'tcp', }, { 'name': 'test2', 'port': 54322, 'real_port': 54322, 'proto': 'udp', }], 'ephemeral_ports': { 'tcp': [10000, 10001, 10002], 'udp': [], }, 'passthrough': [ 'xxx', 'yyy', 'zzz', ], 'vring': { 'some': 'data' } }) app_unique_name = appcfg.app_unique_name(app) hosts_to_ip = { 'xxx': '4.4.4.4', 'yyy': '5.5.5.5', 'zzz': '5.5.5.5', } socket.gethostbyname.side_effect = lambda h: hosts_to_ip[h] self.tm_env.rules.get_rules.return_value = set() treadmill.runtime.linux._run._unshare_network(self.tm_env, 'test_container_dir', app) self.tm_env.rules.create_rule.assert_has_calls([ mock.call(chain=iptables.PREROUTING_DNAT, rule=firewall.DNATRule(proto='tcp', dst_ip='172.31.81.67', dst_port=54321, new_ip='192.168.0.2', new_port=54321), owner=app_unique_name), mock.call(chain=iptables.POSTROUTING_SNAT, rule=firewall.SNATRule(proto='tcp', src_ip='192.168.0.2', src_port=54321, new_ip='172.31.81.67', new_port=54321), owner=app_unique_name), mock.call(chain=iptables.PREROUTING_DNAT, rule=firewall.DNATRule(proto='udp', dst_ip='172.31.81.67', dst_port=54322, new_ip='192.168.0.2', new_port=54322), owner=app_unique_name), mock.call(chain=iptables.POSTROUTING_SNAT, rule=firewall.SNATRule(proto='udp', src_ip='192.168.0.2', src_port=54322, new_ip='172.31.81.67', new_port=54322), owner=app_unique_name), mock.call(chain=iptables.PREROUTING_DNAT, rule=firewall.DNATRule(proto='tcp', dst_ip='172.31.81.67', dst_port=10000, new_ip='192.168.0.2', new_port=10000), owner=app_unique_name), mock.call(chain=iptables.PREROUTING_DNAT, rule=firewall.DNATRule(proto='tcp', dst_ip='172.31.81.67', dst_port=10001, new_ip='192.168.0.2', new_port=10001), owner=app_unique_name), mock.call(chain=iptables.PREROUTING_DNAT, rule=firewall.DNATRule(proto='tcp', dst_ip='172.31.81.67', dst_port=10002, new_ip='192.168.0.2', new_port=10002), owner=app_unique_name), mock.call(chain=iptables.PREROUTING_PASSTHROUGH, rule=firewall.PassThroughRule('4.4.4.4', '192.168.0.2'), owner=app_unique_name), mock.call(chain=iptables.PREROUTING_PASSTHROUGH, rule=firewall.PassThroughRule('5.5.5.5', '192.168.0.2'), owner=app_unique_name), ], any_order=True) self.assertEqual(self.tm_env.rules.create_rule.call_count, 9) # Check that infra services + ephemeral ports are in the same set. treadmill.iptables.add_ip_set.assert_has_calls([ mock.call(treadmill.iptables.SET_VRING_CONTAINERS, '192.168.0.2'), mock.call(treadmill.iptables.SET_INFRA_SVC, '192.168.0.2,tcp:54321'), mock.call(treadmill.iptables.SET_INFRA_SVC, '192.168.0.2,tcp:10000'), mock.call(treadmill.iptables.SET_INFRA_SVC, '192.168.0.2,tcp:10001'), mock.call(treadmill.iptables.SET_INFRA_SVC, '192.168.0.2,tcp:10002'), ], any_order=True) treadmill.newnet.create_newnet.assert_called_with( 'id1234.0', '192.168.0.2', '192.168.254.254', None, )
def test__unshare_network_simple(self): """Tests unshare network sequence. """ # Disable W0212: Access to a protected member # pylint: disable=W0212 app = utils.to_obj({ 'type': 'native', 'name': 'proid.test#0', 'uniqueid': 'ID1234', 'environment': 'dev', 'network': { 'veth': 'id1234.0', 'vip': '192.168.1.1', 'gateway': '192.168.254.254', 'external_ip': '172.31.81.67', }, 'shared_ip': True, 'ephemeral_ports': { 'tcp': [], 'udp': [], }, 'endpoints': [{ 'real_port': '5007', 'proto': 'tcp', 'port': '22', 'type': 'infra' }, { 'real_port': '5013', 'proto': 'udp', 'port': '12345' }], 'vring': { 'some': 'data' } }) app_unique_name = appcfg.app_unique_name(app) treadmill.runtime.linux._run._unshare_network(self.tm_env, 'test_container_dir', app) treadmill.iptables.add_ip_set.assert_has_calls([ mock.call(treadmill.iptables.SET_VRING_CONTAINERS, '192.168.1.1'), mock.call(treadmill.iptables.SET_INFRA_SVC, '192.168.1.1,tcp:22'), ], any_order=True) self.tm_env.rules.create_rule.assert_has_calls([ mock.call(chain=iptables.PREROUTING_DNAT, rule=firewall.DNATRule(proto='tcp', dst_ip='172.31.81.67', dst_port='5007', new_ip='192.168.1.1', new_port='22'), owner=app_unique_name), mock.call(chain=iptables.POSTROUTING_SNAT, rule=firewall.SNATRule(proto='tcp', src_ip='192.168.1.1', src_port='22', new_ip='172.31.81.67', new_port='5007'), owner=app_unique_name), mock.call(chain=iptables.PREROUTING_DNAT, rule=firewall.DNATRule(proto='udp', dst_ip='172.31.81.67', dst_port='5013', new_ip='192.168.1.1', new_port='12345'), owner=app_unique_name), mock.call(chain=iptables.POSTROUTING_SNAT, rule=firewall.SNATRule(proto='udp', src_ip='192.168.1.1', src_port='12345', new_ip='172.31.81.67', new_port='5013'), owner=app_unique_name) ], any_order=True) self.assertEqual(self.tm_env.rules.create_rule.call_count, 4) treadmill.newnet.create_newnet.assert_called_with( 'id1234.0', '192.168.1.1', '192.168.254.254', '172.31.81.67', )
def run(routing, endpoints, discovery, rulemgr, ip_owner, rules_owner): """Manage ring rules based on discovery info. :param routing: The map between logical endpoint name and internal container port that is used for this endpoint. :param endpoints: The set of endpoints to monitor. :param discovery: The treadmill.discovery object/iterator. Loop over discovery never ends, and it yields results in a form: appname:endpoint hostname:port appname:endpoint Absense of hostname:port indicates that given endpoint no longer exists. :param ``RuleMgr`` rulemgr: Firewall rule manager instance. :param ``str`` rules_owner: Unique name of the container owning all the rules. :param ``str`` ip_owner: IP of the container owning of the VRing. """ local_host = sysinfo.hostname() local_ip = socket.gethostbyname(local_host) _LOGGER.info('Starting vring: %r %r %r %r %r', local_host, ip_owner, rules_owner, routing, endpoints) # Add reflective rules back to the container for endpoint in endpoints: dnat_rule = firewall.DNATRule(proto=routing[endpoint]['proto'], src_ip=ip_owner, dst_ip=local_ip, dst_port=routing[endpoint]['port'], new_ip=ip_owner, new_port=routing[endpoint]['port']) rulemgr.create_rule(chain=iptables.VRING_DNAT, rule=dnat_rule, owner=rules_owner) vring_state = {} for (app, hostport) in discovery.iteritems(): # app is in the form appname:endpoint. We care only about endpoint # name. _name, proto, endpoint = app.split(':') # Ignore if endpoint is not in routing (only interested in endpoints # that are in routing table). if endpoint not in endpoints: continue private_port = int(routing[endpoint]['port']) if hostport: host, public_port = hostport.split(':') if host == local_host: continue try: ipaddr = socket.gethostbyname(host) except socket.gaierror as err: _LOGGER.warning('Error resolving %r(%s), skipping.', host, err) continue public_port = int(public_port) vring_route = (proto, ipaddr, public_port) _LOGGER.info('add vring route: %r', vring_route) vring_state[app] = vring_route dnat_rule = firewall.DNATRule(proto=proto, src_ip=ip_owner, dst_ip=ipaddr, dst_port=private_port, new_ip=ipaddr, new_port=public_port) snat_rule = firewall.SNATRule(proto=proto, src_ip=ipaddr, src_port=public_port, dst_ip=ip_owner, new_ip=ipaddr, new_port=private_port) rulemgr.create_rule(chain=iptables.VRING_DNAT, rule=dnat_rule, owner=rules_owner) rulemgr.create_rule(chain=iptables.VRING_SNAT, rule=snat_rule, owner=rules_owner) else: vring_route = vring_state.pop(app, None) if not vring_route: continue _LOGGER.info('del vring route: %r', vring_route) proto, ipaddr, public_port = vring_route dnat_rule = firewall.DNATRule(proto=proto, src_ip=ip_owner, dst_ip=ipaddr, dst_port=private_port, new_ip=ipaddr, new_port=public_port) snat_rule = firewall.SNATRule( proto=proto, src_ip=ipaddr, src_port=public_port, dst_ip=ip_owner, new_ip=ipaddr, new_port=private_port, ) rulemgr.unlink_rule(chain=iptables.VRING_DNAT, rule=dnat_rule, owner=rules_owner) rulemgr.unlink_rule(chain=iptables.VRING_SNAT, rule=snat_rule, owner=rules_owner)
def _cleanup_network(tm_env, container_dir, app, network_client): """Cleanup the network part of a container. """ # Generate a unique name for the app unique_name = appcfg.app_unique_name(app) try: app_network = network_client.get(unique_name) except services.ResourceServiceError: _LOGGER.warning('network never allocated') return if app_network is None: _LOGGER.info('Network resource already freed') return # Unconfigure passthrough if hasattr(app, 'passthrough'): _LOGGER.info('Deleting passthrough for: %r', app.passthrough) # Resolve all the hosts # FIXME: There is no guarantie the hosts will resolve to # the same IPs as they did during creation. ips = set([socket.gethostbyname(host) for host in app.passthrough]) for ip in ips: tm_env.rules.unlink_rule( chain=iptables.PREROUTING_PASSTHROUGH, rule=firewall.PassThroughRule(src_ip=ip, dst_ip=app_network['vip']), owner=unique_name, ) if app.vring: # Mark the container's IP as VRing enabled _LOGGER.debug('removing %r from VRing set', app_network['vip']) iptables.rm_ip_set(iptables.SET_VRING_CONTAINERS, app_network['vip']) for endpoint in app.endpoints: tm_env.rules.unlink_rule( chain=iptables.PREROUTING_DNAT, rule=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), owner=unique_name, ) tm_env.rules.unlink_rule( chain=iptables.POSTROUTING_SNAT, rule=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), owner=unique_name, ) # See if this was an "infra" endpoint and if so remove it # from the whitelist set. if getattr(endpoint, 'type', None) == 'infra': _LOGGER.debug('removing %s:%s from infra services set', app_network['vip'], endpoint.port) iptables.rm_ip_set( iptables.SET_INFRA_SVC, '{ip},{proto}:{port}'.format( ip=app_network['vip'], proto=endpoint.proto, port=endpoint.port, )) _cleanup_ephemeral_ports(tm_env, unique_name, app_network['external_ip'], app_network['vip'], app.ephemeral_ports.tcp, 'tcp') _cleanup_ephemeral_ports(tm_env, unique_name, app_network['external_ip'], app_network['vip'], app.ephemeral_ports.udp, 'udp') _cleanup_exception_rules(tm_env, container_dir, app) # Terminate any entries in the conntrack table iptables.flush_cnt_conntrack_table(app_network['vip']) # Cleanup network resources network_client.delete(unique_name)