Support ip6tables for iptables pxe filter

Adds a configuration option [iptables]ip_version to specify the
desired ip version for the iptables pxe filter, which can be set to
4 or 6. When set to 6, the iptables pxe filter will use ip6tables
command to manage rules for the port 547 which is the port of DHCPv6
server side.

The string type is used to make room for the future, when there is need
to automatically determine ip version from the binding interface.

Change-Id: I7de2be5950a23def3ec6490f2e6dfa3d5c42798a
Story: 1756012
Task: 11411
This commit is contained in:
Kaifeng Wang 2018-11-06 13:42:29 +08:00
parent 054f300290
commit f37eb0fc58
5 changed files with 85 additions and 17 deletions

View File

@ -34,6 +34,12 @@ _OPTS = [
'which are not in desired state are going to be '
'blacklisted based on the list of neighbor MACs '
'on these interfaces.')),
cfg.StrOpt('ip_version',
default='4',
choices=[('4', _('IPv4')),
('6', _('IPv6'))],
help=_('The IP version that will be used for iptables filter. '
'Defaults to 4.')),
]

View File

@ -50,8 +50,18 @@ class IptablesFilter(pxe_filter.BaseFilter):
self.interface = CONF.iptables.dnsmasq_interface
self.chain = CONF.iptables.firewall_chain
self.new_chain = self.chain + '_temp'
# Determine arguments used for pxe filtering, we only support 4 and 6
# at this time.
if CONF.iptables.ip_version == '4':
self._cmd_iptables = 'iptables'
self._dhcp_port = '67'
else:
self._cmd_iptables = 'ip6tables'
self._dhcp_port = '547'
self.base_command = ('sudo', 'ironic-inspector-rootwrap',
CONF.rootwrap_config, 'iptables')
CONF.rootwrap_config, self._cmd_iptables)
def reset(self):
self.enabled = True
@ -137,9 +147,9 @@ class IptablesFilter(pxe_filter.BaseFilter):
# Swap chains
self._iptables('-I', 'INPUT', '-i', self.interface, '-p', 'udp',
'--dport', '67', '-j', chain)
'--dport', self._dhcp_port, '-j', chain)
self._iptables('-D', 'INPUT', '-i', self.interface, '-p', 'udp',
'--dport', '67', '-j', main_chain,
'--dport', self._dhcp_port, '-j', main_chain,
ignore=True)
self._iptables('-F', main_chain, ignore=True)
self._iptables('-X', main_chain, ignore=True)
@ -163,7 +173,7 @@ class IptablesFilter(pxe_filter.BaseFilter):
def _clean_up(self, chain):
self._iptables('-D', 'INPUT', '-i', self.interface, '-p', 'udp',
'--dport', '67', '-j', chain,
'--dport', self._dhcp_port, '-j', chain,
ignore=True)
self._iptables('-F', chain, ignore=True)
self._iptables('-X', chain, ignore=True)

View File

@ -114,20 +114,23 @@ class TestIptablesDriver(test_base.NodeTest):
self.assertRaisesRegex(MyError, 'Oops!', self.driver.init_filter)
self.check_fsm([pxe_filter.Events.initialize, pxe_filter.Events.reset])
def test__iptables_args(self):
def _test__iptables_args(self, expected_port):
self.driver = iptables.IptablesFilter()
self.mock_iptables = self.useFixture(
fixtures.MockPatchObject(self.driver, '_iptables')).mock
self.mock_should_enable_dhcp.return_value = True
_iptables_expected_args = [
('-D', 'INPUT', '-i', 'br-ctlplane', '-p', 'udp', '--dport',
'67', '-j', self.driver.new_chain),
expected_port, '-j', self.driver.new_chain),
('-F', self.driver.new_chain),
('-X', self.driver.new_chain),
('-N', self.driver.new_chain),
('-A', self.driver.new_chain, '-j', 'ACCEPT'),
('-I', 'INPUT', '-i', 'br-ctlplane', '-p', 'udp', '--dport',
'67', '-j', self.driver.new_chain),
expected_port, '-j', self.driver.new_chain),
('-D', 'INPUT', '-i', 'br-ctlplane', '-p', 'udp', '--dport',
'67', '-j', self.driver.chain),
expected_port, '-j', self.driver.chain),
('-F', self.driver.chain),
('-X', self.driver.chain),
('-E', self.driver.new_chain, self.driver.chain)
@ -142,6 +145,14 @@ class TestIptablesDriver(test_base.NodeTest):
self.mock__get_blacklist.assert_called_once_with(self.mock_ironic)
self.check_fsm([pxe_filter.Events.sync])
def test__iptables_args_ipv4(self):
CONF.set_override('ip_version', '4', 'iptables')
self._test__iptables_args('67')
def test__iptables_args_ipv6(self):
CONF.set_override('ip_version', '6', 'iptables')
self._test__iptables_args('547')
def test__iptables_kwargs(self):
_iptables_expected_kwargs = [
{'ignore': True},
@ -163,13 +174,16 @@ class TestIptablesDriver(test_base.NodeTest):
self.assertEqual(kwargs, call[1])
self.check_fsm([pxe_filter.Events.sync])
def test_sync_with_blacklist(self):
def _test_sync_with_blacklist(self, expected_port):
self.driver = iptables.IptablesFilter()
self.mock_iptables = self.useFixture(
fixtures.MockPatchObject(self.driver, '_iptables')).mock
self.mock__get_blacklist.return_value = ['AA:BB:CC:DD:EE:FF']
self.mock_should_enable_dhcp.return_value = True
_iptables_expected_args = [
('-D', 'INPUT', '-i', 'br-ctlplane', '-p', 'udp', '--dport',
'67', '-j', self.driver.new_chain),
expected_port, '-j', self.driver.new_chain),
('-F', self.driver.new_chain),
('-X', self.driver.new_chain),
('-N', self.driver.new_chain),
@ -178,9 +192,9 @@ class TestIptablesDriver(test_base.NodeTest):
self.mock__get_blacklist.return_value[0], '-j', 'DROP'),
('-A', self.driver.new_chain, '-j', 'ACCEPT'),
('-I', 'INPUT', '-i', 'br-ctlplane', '-p', 'udp', '--dport',
'67', '-j', self.driver.new_chain),
expected_port, '-j', self.driver.new_chain),
('-D', 'INPUT', '-i', 'br-ctlplane', '-p', 'udp', '--dport',
'67', '-j', self.driver.chain),
expected_port, '-j', self.driver.chain),
('-F', self.driver.chain),
('-X', self.driver.chain),
('-E', self.driver.new_chain, self.driver.chain)
@ -203,7 +217,18 @@ class TestIptablesDriver(test_base.NodeTest):
self.mock__get_blacklist.assert_called_once_with(self.mock_ironic)
self.assertFalse(self.mock_iptables.called)
def test__iptables_clean_cache_on_error(self):
def test_sync_with_blacklist_ipv4(self):
CONF.set_override('ip_version', '4', 'iptables')
self._test_sync_with_blacklist('67')
def test_sync_with_blacklist_ipv6(self):
CONF.set_override('ip_version', '6', 'iptables')
self._test_sync_with_blacklist('547')
def _test__iptables_clean_cache_on_error(self, expected_port):
self.driver = iptables.IptablesFilter()
self.mock_iptables = self.useFixture(
fixtures.MockPatchObject(self.driver, '_iptables')).mock
self.mock__get_blacklist.return_value = ['AA:BB:CC:DD:EE:FF']
self.mock_should_enable_dhcp.return_value = True
@ -217,7 +242,7 @@ class TestIptablesDriver(test_base.NodeTest):
syncs_expected_args = [
# driver reset
('-D', 'INPUT', '-i', 'br-ctlplane', '-p', 'udp', '--dport',
'67', '-j', self.driver.new_chain),
expected_port, '-j', self.driver.new_chain),
('-F', self.driver.new_chain),
('-X', self.driver.new_chain),
('-N', self.driver.new_chain),
@ -226,9 +251,9 @@ class TestIptablesDriver(test_base.NodeTest):
self.mock__get_blacklist.return_value[0], '-j', 'DROP'),
('-A', self.driver.new_chain, '-j', 'ACCEPT'),
('-I', 'INPUT', '-i', 'br-ctlplane', '-p', 'udp', '--dport',
'67', '-j', self.driver.new_chain),
expected_port, '-j', self.driver.new_chain),
('-D', 'INPUT', '-i', 'br-ctlplane', '-p', 'udp', '--dport',
'67', '-j', self.driver.chain),
expected_port, '-j', self.driver.chain),
('-F', self.driver.chain),
('-X', self.driver.chain),
('-E', self.driver.new_chain, self.driver.chain)
@ -247,6 +272,24 @@ class TestIptablesDriver(test_base.NodeTest):
self.assertEqual(args, call[0], 'idx: %s' % idx)
self.mock__get_blacklist.assert_called_once_with(self.mock_ironic)
def test__iptables_clean_cache_on_error_ipv4(self):
CONF.set_override('ip_version', '4', 'iptables')
self._test__iptables_clean_cache_on_error('67')
def test__iptables_clean_cache_on_error_ipv6(self):
CONF.set_override('ip_version', '6', 'iptables')
self._test__iptables_clean_cache_on_error('547')
def test_iptables_command_ipv4(self):
CONF.set_override('ip_version', '4', 'iptables')
driver = iptables.IptablesFilter()
self.assertEqual(driver._cmd_iptables, 'iptables')
def test_iptables_command_ipv6(self):
CONF.set_override('ip_version', '6', 'iptables')
driver = iptables.IptablesFilter()
self.assertEqual(driver._cmd_iptables, 'ip6tables')
class Test_ShouldEnableDhcp(test_base.BaseTest):
def setUp(self):

View File

@ -0,0 +1,8 @@
---
features:
- |
Adds a configuration option ``[iptables]ip_version`` to specify the
desired ip version for the iptables pxe filter, possible values are ``4``
and ``6``, the default value is ``4``. When set to ``6``, the iptables
pxe filter will use ``ip6tables`` command to manage rules for the DHCPv6
port ``547``.

View File

@ -2,8 +2,9 @@
[Filters]
# ironic-inspector-rootwrap command filters for firewall manipulation
# ironic_inspector/firewall.py
# ironic_inspector/pxe_filter/iptables.py
iptables: CommandFilter, iptables, root
ip6tables: CommandFilter, ip6tables, root
# ironic-inspector-rootwrap command filters for systemctl manipulation of the dnsmasq service
# ironic_inspector/pxe_filter/dnsmasq.py