Browse Source

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
changes/28/615728/3
Kaifeng Wang 4 years ago
parent
commit
f37eb0fc58
  1. 6
      ironic_inspector/conf/iptables.py
  2. 18
      ironic_inspector/pxe_filter/iptables.py
  3. 67
      ironic_inspector/test/unit/test_iptables.py
  4. 8
      releasenotes/notes/support-ip6tables-ce30f614de502adb.yaml
  5. 3
      rootwrap.d/ironic-inspector.filters

6
ironic_inspector/conf/iptables.py

@ -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.')),
]

18
ironic_inspector/pxe_filter/iptables.py

@ -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)

67
ironic_inspector/test/unit/test_iptables.py

@ -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):

8
releasenotes/notes/support-ip6tables-ce30f614de502adb.yaml

@ -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``.

3
rootwrap.d/ironic-inspector.filters

@ -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

Loading…
Cancel
Save